# WorkflowEngine Documentation > Documentation for WorkflowEngine — a workflow automation framework for .NET applications. Contains guides, API references, tutorials, and release notes for building workflow applications. Get a free trial key at https://trial.workflowengine.io/. ## documentation Workflow Get Started - [Get Started](https://workflowengine.io/documentation/): Workflow Get Started ### api A Workflow Engine Web API - [Workflow Engine API (1.0)](https://workflowengine.io/documentation/api): A Workflow Engine Web API ### api-reference - [Loading API Reference](https://workflowengine.io/documentation/api-reference) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/ActiveDirectoryPlugin) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/BpmnPlugin) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/DbPersistence) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/FilesPlugin) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/FormsPlugin) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Migrator) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/MongoDB) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/MySQL) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Oracle) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/PostgreSQL) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/RealTimeTrackingPlugin) - [Loading API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/SQLite) ### search - [Search the documentation](https://workflowengine.io/documentation/search) ### bpmn BPMN support in Workflow Engine - [BPMN support in Workflow Engine](https://workflowengine.io/documentation/bpmn): BPMN support in Workflow Engine ### bpmn-differences Key differences between BPMN Diagrams and Workflow Engine process schemes - [Key differences between BPMN diagrams and Workflow Engine process schemes](https://workflowengine.io/documentation/bpmn-differences): Key differences between BPMN Diagrams and Workflow Engine process schemes ### bpmn-elements BPMN elements support in Workflow Engine - [BPMN elements support in Workflow Engine](https://workflowengine.io/documentation/bpmn-elements): BPMN elements support in Workflow Engine ### bpmn-plugin Configuring and using BPMN plugin - [Configuring and using BPMN plugin](https://workflowengine.io/documentation/bpmn-plugin): Configuring and using BPMN plugin ### category - [Guides](https://workflowengine.io/documentation/category/guides): Have a look at Workflow Engine! ### creating-a-workflow-schema-using-code In this guide we will look at how to create a workflow schema using code (programmatically). - [Creating a workflow schema using code](https://workflowengine.io/documentation/creating-a-workflow-schema-using-code): In this guide we will look at how to create a workflow schema using code (programmatically). ### db-entities This section describes the Workflow Engine database objects. All objects are inside the dbo schema. - [MS SQL Server Relational Database](https://workflowengine.io/documentation/db-entities): This section describes the Workflow Engine database objects. All objects are inside the dbo schema. - [Procedures](https://workflowengine.io/documentation/db-entities/procedures): 1. DropUnusedWorkflowProcessScheme - [DropUnusedWorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/procedures/DropUnusedWorkflowProcessScheme): Reference - [DropWorkflowInbox](https://workflowengine.io/documentation/db-entities/procedures/DropWorkflowInbox): Reference - [spWorkflowProcessResetRunningStatus](https://workflowengine.io/documentation/db-entities/procedures/spWorkflowProcessResetRunningStatus): Reference - [Tables](https://workflowengine.io/documentation/db-entities/tables): 1. WorkflowApprovalHistory - [WorkflowApprovalHistory](https://workflowengine.io/documentation/db-entities/tables/WorkflowApprovalHistory): Entity - [WorkflowGlobalParameter](https://workflowengine.io/documentation/db-entities/tables/WorkflowGlobalParameter): Entity - [WorkflowInbox](https://workflowengine.io/documentation/db-entities/tables/WorkflowInbox): Entity - [WorkflowProcessAssignment](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessAssignment): Entity - [WorkflowProcessInstance](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstance): Entity - [WorkflowProcessInstancePersistence](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstancePersistence): Entity - [WorkflowProcessInstanceStatus](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstanceStatus): Entity - [WorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessScheme): Entity - [WorkflowProcessTimer](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessTimer): Entity - [WorkflowProcessTransitionHistory](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessTransitionHistory): Entity - [WorkflowRuntime](https://workflowengine.io/documentation/db-entities/tables/WorkflowRuntime): Entity - [WorkflowScheme](https://workflowengine.io/documentation/db-entities/tables/WorkflowScheme): Entity - [WorkflowSync](https://workflowengine.io/documentation/db-entities/tables/WorkflowSync): Entity ### demo-description Get acquainted with main concepts in Workflow Engine Demo application - [Demo application](https://workflowengine.io/documentation/demo-description): Get acquainted with main concepts in Workflow Engine Demo application ### designer-customization This section describes the Workflow Engine functions using which you can change designer appearance and behaviour and make it more user-friendly. - [Designer customization](https://workflowengine.io/documentation/designer-customization): This section describes the Workflow Engine functions using which you can change designer appearance and behaviour and make it more user-friendly. - [Designer UI Customization](https://workflowengine.io/documentation/designer-customization/appearance-customization): Take a walkthrough of the tutorial on customizing the Workflow Engine designer's user interface. - [Autocomplete setting in the designer using IDesignerAutocompleteProvider](https://workflowengine.io/documentation/designer-customization/autocomplete-provider): Sometimes it is very useful to give a small tip to the user, who is editing the scheme, to prompt him which value to specify for the parameter transferred into Action, Condition or Rule. - [Custom Action parameter control](https://workflowengine.io/documentation/designer-customization/custom-action-parameter-control): Custom CodeActions parameter control - [Custom Activity](https://workflowengine.io/documentation/designer-customization/custom-activity): Custom Activity - [Languages](https://workflowengine.io/documentation/designer-customization/languages): Custom languages - [Parameter edit form for Action, Condition or Rule](https://workflowengine.io/documentation/designer-customization/parameter-appearance): Parameter which is transferred into Action, Condition or Rule can be a complex JSON object, that is why it might be useful to help user fill in this object. ### dynamic-plugin-loading In this tutorial, we'll implement dynamic plugin loading. - [Dynamic plugin loading](https://workflowengine.io/documentation/dynamic-plugin-loading): In this tutorial, we'll implement dynamic plugin loading. ### execution This section describes everything that relates to the order of process execution in Workflow Engine. - [Execution](https://workflowengine.io/documentation/execution): This section describes everything that relates to the order of process execution in Workflow Engine. - [Builder steps](https://workflowengine.io/documentation/execution/build-steps): Build steps is an additional opportunity to change a process scheme when creating it. - [Pre-execution](https://workflowengine.io/documentation/execution/pre-execution): Workflow Engine has a mode that simulates process execution, required to track the future path of process execution and leave some artefacts in your system. - [Lifecycle of a regular process](https://workflowengine.io/documentation/execution/regular-process): Learn about the order of execution of a process, beginning with its initialization and ending up with it reaching one of the final states. - [Scheme generation](https://workflowengine.io/documentation/execution/scheme-generation): Scheme generator provides ample opportunities for enhancing Workflow Engine's operation. Learn more about scheme generation in Workflow Engine. - [Scheme inlining](https://workflowengine.io/documentation/execution/scheme-inlining): Scheme inlining is embedding one scheme into another. - [Schema versioning](https://workflowengine.io/documentation/execution/scheme-update): Get acquainted with the two strategies for updating the schemes of running processes Workflow Engine provides. - [Setting process state](https://workflowengine.io/documentation/execution/setting-state-activity): In this article are described the methods for setting the State or Activity from a process - [Subprocesses/Parallel](https://workflowengine.io/documentation/execution/subprocesses): Learn how to create parallel branches in Workflow Engine by getting acquainted with Subprocesses, their boundaries and limitations. ### faq - [FAQ: Workflow Engine](https://workflowengine.io/documentation/faq/workflow-engine): * How to choose? Workflow Engine or Workflow Server? - [Can I assign multiple users to approve of a process?](https://workflowengine.io/documentation/faq/workflow-engine/assigning-multiple-users-to-approve-of-a-document-or-process): Yes, you can assign multiple users to approve of a document/process. In fact, you have two options. - [Designer troubleshooting](https://workflowengine.io/documentation/faq/workflow-engine/designer-troubleshooting): Read about the problems with Designer you may encounter in Workflow Engine and learn how to tackle them. - [How to call a code of my assembly from CodeAction](https://workflowengine.io/documentation/faq/workflow-engine/how-to-call-a-code-of-my-assembly-from-codeaction): Learn how to call a code of my assembly from CodeAction in Workflow Engine. - [How to create a dialog for executing a command with parameters?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-dialog-for-command-execution): You can learn how to implement a dialog to execute a command with parameters in this tutorial. - [How to create document history for status changes](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-document-history-for-status-changes): Learn how to create document history for status changes in Workflow Engine. - [How to Create Dynamic Timers](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-dynamic-timers): Use expression timers with parameters - [How to create parallel approval within a single stage](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-parallel-approval-within-a-single-stage): Learn how to create parallel approval within a single stage in Workflow Engine. - [How to create 'Wait for condition'](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-wait-for-condition): Learn how to create Wait for condition in Workflow Engine. - [How to add a drop-down (select) with a stored value to an Activity form](https://workflowengine.io/documentation/faq/workflow-engine/how-to-dropdown-to-activity-form): How to add a drop-down (select) with a stored value to an Activity form - [How to invite/get all users who can approve a document](https://workflowengine.io/documentation/faq/workflow-engine/how-to-invite-get-all-users-who-can-approve-a-document): Learn how to invite/get all users who can approve a document in Workflow Engine. - [How to obtain process lists for inbox/outbox folders](https://workflowengine.io/documentation/faq/workflow-engine/how-to-obtain-process-lists-for-inbox-outbox-folders): Learn how to obtain process lists for inbox/outbox folders in Workflow Engine. - [How to register a license key](https://workflowengine.io/documentation/faq/workflow-engine/how-to-register-a-license-key): Learn how register a license key in Workflow Engine. - [How to set document status](https://workflowengine.io/documentation/faq/workflow-engine/how-to-set-document-status): Learn how to get the name of the status and set document status in Workflow Engine. - [How to config timer by number of working days? E.g. trigger a reminder e-mail after 5 working days](https://workflowengine.io/documentation/faq/workflow-engine/how-to-set-working-days-in-timers): Starting with WFE 11.0 you can use work calendars for this task - [I got an error 'CS...' (C# Compiler) in runtime](https://workflowengine.io/documentation/faq/workflow-engine/i-got-an-error-cs-in-runtime): Learn what to do when you get an CS... (C# Compiler) error in runtime. - [Can I install Workflow Engine Enterprise on multiple servers?](https://workflowengine.io/documentation/faq/workflow-engine/installing-workflow-engine-enterprise-on-multiple-servers): Workflow Engine Enterprise license does not put any limitations on the number of end-users and servers using the application. - [Can I integrate Workflow Designer into an Angular or React app?](https://workflowengine.io/documentation/faq/workflow-engine/integrate-workflow-engine-into-angular-or-react): Workflow Engine's Designer can be integrated into any HTML page, since it runs on JavaScript. - [Is it possible to run Workflow Designer in Blazor?](https://workflowengine.io/documentation/faq/workflow-engine/is-it-possible-to-run-workflow-designer-in-blazor): You can find out how to run the Workflow Engine Designer in Blazor - [Is the execution of the process thread-safe or transactional?](https://workflowengine.io/documentation/faq/workflow-engine/is-process-execution-thread-safe): The execution of a process in the Workflow Engine is thread-safe but not transactional. - [Is Workflow Engine SaaS compliant? Can it run in the cloud?](https://workflowengine.io/documentation/faq/workflow-engine/is-workflow-engine-saas-compliant): Workflow Engine can be easily integrated into any SaaS application and can run on any cloud environment. - [Does the WorkflowInit class need to be static? How can I use the dependency injection in this case?](https://workflowengine.io/documentation/faq/workflow-engine/is-workflowinit-class-static): Using the WorkflowInit class with dependency injection - [How to Manage User Roles in WorkflowEngine?](https://workflowengine.io/documentation/faq/workflow-engine/manage-roles): How to Manage User Roles in WorkflowEngine? - [My workflow is completed just after I have started it](https://workflowengine.io/documentation/faq/workflow-engine/my-workflow-is-completed-just-after-i-have-started-it): Learn what to do when your workflow is completed just after you have started it. - [Which .NET version is required for Core & Designer?](https://workflowengine.io/documentation/faq/workflow-engine/net-framework-for-core-and-designer): Workflow Engine runs on .NET Framework 4.6.2 and higher or Microsoft.NETCoreApp version higher than 1.1. - [How to satisfy strict CSP in Workflow Designer?](https://workflowengine.io/documentation/faq/workflow-engine/strict-csp-designer): Enabling Workflow Designer without CSS-in-JS for strict Content Security Policy. - [Can I trigger a child process from a parent process?](https://workflowengine.io/documentation/faq/workflow-engine/trigger-a-child-process-from-a-parent-process): Yes, you can. Use the API to work with processes from the Actions code. - [Can I trigger a child scheme from a parent scheme?](https://workflowengine.io/documentation/faq/workflow-engine/trigger-a-child-scheme-from-a-parent-scheme): Currently you can not. We’re working on delivering an update with this functionality. - [Error CS0012: The type 'Object' is defined in an assembly that is not referenced](https://workflowengine.io/documentation/faq/workflow-engine/unreferenced-assembly-error): Read about the unreferenced assembly error upon your Workflow Engine project compilation. - [How to choose? Workflow Engine or Workflow Server?](https://workflowengine.io/documentation/faq/workflow-engine/workflow-server-vs-workflow-engine): If you are wondering which solution to choose between Workflow Engine and Workflow Server, this guide will help you make an informed decision. ### forms Forms support in Workflow Engine - [Forms and Workflow](https://workflowengine.io/documentation/forms): Forms support in Workflow Engine ### forms-connection How to connect forms to Workflow Engine - [How to Connect Forms to Workflow Engine](https://workflowengine.io/documentation/forms-connection): How to connect forms to Workflow Engine ### forms-features Forms Plugin features description - [Forms Plugin Features](https://workflowengine.io/documentation/forms-features): Forms Plugin features description ### forms-sample Sample project for working with Forms Plugin - [Sample project](https://workflowengine.io/documentation/forms-sample): Sample project for working with Forms Plugin ### guides - [How to choose the right embedded workflow automation tool?](https://workflowengine.io/documentation/guides/camuda-7-vs-workflow-engine): Using the course payment process as an illustration, this post will compare Workflow Engine with Camunda 7, as a solution. ### how-to-integrate Also check out the integration tips section. - [Framework-agnostic Setup](https://workflowengine.io/documentation/how-to-integrate): Also check out the integration tips section. ### how-to-test-workflow-schemes Following this guide, you'll write automated tests for the Vacation request workflow scheme. - [How to test workflow schemes](https://workflowengine.io/documentation/how-to-test-workflow-schemes): Following this guide, you'll write automated tests for the Vacation request workflow scheme. ### integrations-tips In this section you will find tips on integrating Workflow Engine. - [Integration tips](https://workflowengine.io/documentation/integrations-tips): In this section you will find tips on integrating Workflow Engine. ### license-key How to get a license key - [License key](https://workflowengine.io/documentation/license-key): How to get a license key ### main-terms - [WorkflowRuntime: Process Management](https://workflowengine.io/documentation/main-terms/basic-operations): This Section describes basic workflow management functionality. In fact, there are far more ways of managing workflows - [Database Versioning](https://workflowengine.io/documentation/main-terms/database-versioning): Starting from version 13.0.0, Workflow Engine supports automatic migration, enabling the creation and updating of the database schema - [Designer](https://workflowengine.io/documentation/main-terms/designer): Examples of Designer integration - [Persistence Provider Initialization](https://workflowengine.io/documentation/main-terms/persistence): First, it is necessary to determine whether you need an SQL or NoSQL database and download the necessary provider from - [WorkflowRuntime Initialization](https://workflowengine.io/documentation/main-terms/runtime): The second thing you should do, when integrating Workflow Engine into your project, is to initialize - [WorkflowRuntimeSettings: Configuration](https://workflowengine.io/documentation/main-terms/runtime-settings): This section describes the fields that might be implemented in the class WorkflowRuntimeSettings and also by ### migration-vue-wfe20 Starting with `Workflow Engine 20.0.0` templates are based on `Vue 3`, this guide shows how to adapt your customizations. - [Migration to Vue 3](https://workflowengine.io/documentation/migration-vue-wfe20): Starting with `Workflow Engine 20.0.0` templates are based on `Vue 3`, this guide shows how to adapt your customizations. ### parallel-approval-without-branches In this tutorial, we'll make an example of a parallel approval method without branching. - [Parallel approval without branches](https://workflowengine.io/documentation/parallel-approval-without-branches): In this tutorial, we'll make an example of a parallel approval method without branching. ### plugins General information - [Plugins](https://workflowengine.io/documentation/plugins): General information - [Active Directory / Entra ID Plugin](https://workflowengine.io/documentation/plugins/activedirectoryplugin): With this plugin, you will be able to connect to Active Directory / Entra ID via LDAP. Microsoft renamed Azure Active Directory (Azure AD) - [Approval Plugin](https://workflowengine.io/documentation/plugins/approvalplugin): The Plugin implements functions for handling the Approval history of your document - [Approval Plugin API](https://workflowengine.io/documentation/plugins/approvalplugin-api): The Plugin implements functions for handling - [Assignment Plugin](https://workflowengine.io/documentation/plugins/assignmentplugin): This plugin implements the basic functions for handling assignments. Assignments are an alternative way to - [Assignment Plugin API](https://workflowengine.io/documentation/plugins/assignmentplugin-api): This Plugin implements the basic functions for working with assignments. - [Basic Plugin](https://workflowengine.io/documentation/plugins/basicplugin): This Plugin implements the most common functions for handling processes, parameters, etc. - [Basic Plugin API](https://workflowengine.io/documentation/plugins/basicplugin-api): This Plugin implements the most common functions for handling processes, parameters, etc. - [File Plugin](https://workflowengine.io/documentation/plugins/fileplugin): This plugin implements functions used for file handling. - [File Plugin API](https://workflowengine.io/documentation/plugins/fileplugin-api): This plugin implements functions used for file handling. - [Loops Plugin](https://workflowengine.io/documentation/plugins/loopsplugin): This Plugin implements functions for handling loops in your scheme. - [Loops Plugin API](https://workflowengine.io/documentation/plugins/loopsplugin-api): This Plugin implements functions for handling loops in your scheme. - [Real Time Tracking Plugin](https://workflowengine.io/documentation/plugins/realtimetrackingplugin): The Real Time Tracking Plugin enhances the user experience by providing instant updates on process instances without the need for page ### process-parameters-sample Process parameters implementation in React sample application - [Introducing parameters](https://workflowengine.io/documentation/process-parameters-sample): Process parameters implementation in React sample application ### react-integration-from-scratch In this guide we will explain how to integrate Workflow Engine with React. - [React integration from scratch](https://workflowengine.io/documentation/react-integration-from-scratch): In this guide we will explain how to integrate Workflow Engine with React. ### react-sample-docker The required steps to run React sample in Docker Compose are described in this guide - [React sample in Docker](https://workflowengine.io/documentation/react-sample-docker): The required steps to run React sample in Docker Compose are described in this guide ### release-notes The official release notes detailing updates, new features, bug fixes, and version changes for the Workflow Engine platform. - [Release notes](https://workflowengine.io/documentation/release-notes/): The official release notes detailing updates, new features, bug fixes, and version changes for the Workflow Engine platform. - [Workflow Engine 1.5.2](https://workflowengine.io/documentation/release-notes/1.5.2): Workflow Engine 1.5.2 release notes - [Workflow Engine 1.5.3](https://workflowengine.io/documentation/release-notes/1.5.3): Workflow Engine 1.5.3 release notes - [Workflow Engine 1.5.4](https://workflowengine.io/documentation/release-notes/1.5.4): Workflow Engine 1.5.4 release notes - [Workflow Engine 1.5.5](https://workflowengine.io/documentation/release-notes/1.5.5): Workflow Engine 1.5.5 release notes - [Workflow Engine 1.5.6](https://workflowengine.io/documentation/release-notes/1.5.6): Workflow Engine 1.5.6 release notes - [Workflow Engine 10.0.0](https://workflowengine.io/documentation/release-notes/10.0.0): Release date: July 13, 2023 - [Workflow Engine 10.0.1](https://workflowengine.io/documentation/release-notes/10.0.1): Release date: July 13, 2023 - [Workflow Engine 11.0.0](https://workflowengine.io/documentation/release-notes/11.0.0): Release date: August 28, 2023 - [Workflow Engine 11.0.1](https://workflowengine.io/documentation/release-notes/11.0.1): Release date: September 01, 2023 - [Workflow Engine 12.0.0](https://workflowengine.io/documentation/release-notes/12.0.0): Release date: December 04, 2023 - [Workflow Engine 12.1.0](https://workflowengine.io/documentation/release-notes/12.1.0): Release date: December 25, 2023 - [Workflow Engine 12.1.1](https://workflowengine.io/documentation/release-notes/12.1.1): Release date: January 19, 2024 - [Workflow Engine 12.2.0](https://workflowengine.io/documentation/release-notes/12.2.0): Release date: February 21, 2024 - [Workflow Engine 12.3.0](https://workflowengine.io/documentation/release-notes/12.3.0): Release date: March 13, 2024 - [Workflow Engine 12.4.0](https://workflowengine.io/documentation/release-notes/12.4.0): Release date: March 28, 2024 - [Workflow Engine 12.5.0](https://workflowengine.io/documentation/release-notes/12.5.0): Release date: April 10, 2024 - [Workflow Engine 12.5.1](https://workflowengine.io/documentation/release-notes/12.5.1): Release date: April 24, 2024 - [Workflow Engine 13.0.0](https://workflowengine.io/documentation/release-notes/13.0.0): Release date: May 6, 2024 - [Workflow Engine 13.1.0](https://workflowengine.io/documentation/release-notes/13.1.0): Release date: May 13, 2024 - [Workflow Engine 13.2.0](https://workflowengine.io/documentation/release-notes/13.2.0): Release date: May 27, 2024 - [Workflow Engine 13.2.1](https://workflowengine.io/documentation/release-notes/13.2.1): Release date: June 25, 2024 - [Workflow Engine 13.2.2](https://workflowengine.io/documentation/release-notes/13.2.2): Release date: July 19, 2024 - [Workflow Engine 13.3.0](https://workflowengine.io/documentation/release-notes/13.3.0): Release date: August 7, 2024 - [Workflow Engine 13.3.1](https://workflowengine.io/documentation/release-notes/13.3.1): Release date: August 12, 2024 - [Workflow Engine 14.0.0](https://workflowengine.io/documentation/release-notes/14.0.0): Release date: August 29, 2024 - [Workflow Engine 14.1.0](https://workflowengine.io/documentation/release-notes/14.1.0): Release date: September 20, 2024 - [Workflow Engine 15.0.0](https://workflowengine.io/documentation/release-notes/15.0.0): Release date: September 25, 2024 - [Workflow Engine 15.0.1](https://workflowengine.io/documentation/release-notes/15.0.1): Release date: September 26, 2024 - [Workflow Engine 16.0.0](https://workflowengine.io/documentation/release-notes/16.0.0): Release date: October 24, 2024 - [Workflow Engine 16.1.0](https://workflowengine.io/documentation/release-notes/16.1.0): Release date: November 1, 2024 - [Workflow Engine 16.2.0](https://workflowengine.io/documentation/release-notes/16.2.0): Release date: December 10, 2024 - [Workflow Engine 16.2.1](https://workflowengine.io/documentation/release-notes/16.2.1): Release date: January 9, 2025 - [Workflow Engine 16.3.0](https://workflowengine.io/documentation/release-notes/16.3.0): Release date: January 23, 2025 - [Workflow Engine 16.4.0](https://workflowengine.io/documentation/release-notes/16.4.0): Release date: March 10, 2025 - [Workflow Engine 17.0.0](https://workflowengine.io/documentation/release-notes/17.0.0): Release date: April 1, 2025 - [Workflow Engine 17.1.0](https://workflowengine.io/documentation/release-notes/17.1.0): Release date: May 23, 2025 - [Workflow Engine 17.2.0](https://workflowengine.io/documentation/release-notes/17.2.0): Release date: June 19, 2025 - [Workflow Engine 17.3.0](https://workflowengine.io/documentation/release-notes/17.3.0): Release date: August 12, 2025 - [Workflow Engine 18.0.0](https://workflowengine.io/documentation/release-notes/18.0.0): Release date: August 18, 2025 - [Workflow Engine 18.1.0](https://workflowengine.io/documentation/release-notes/18.1.0): Release date: August 25, 2025 - [Workflow Engine 19.0.0](https://workflowengine.io/documentation/release-notes/19.0.0): Release date: September 18, 2025 - [Workflow Engine 2.0](https://workflowengine.io/documentation/release-notes/2.0): Workflow Engine 2.0 release notes - [Workflow Engine 2.1](https://workflowengine.io/documentation/release-notes/2.1): Release date: May 16, 2018 - [Workflow Engine 2.2](https://workflowengine.io/documentation/release-notes/2.2): Workflow Engine 2.2 release notes - [Workflow Engine 2.3](https://workflowengine.io/documentation/release-notes/2.3): Release date: October 1, 2018 - [Workflow Engine 20.0.0](https://workflowengine.io/documentation/release-notes/20.0.0): Release date: October 30, 2025 - [Workflow Engine 20.0.1](https://workflowengine.io/documentation/release-notes/20.0.1): Release date: November 10, 2025 - [Workflow Engine 20.0.2](https://workflowengine.io/documentation/release-notes/20.0.2): Release date: November 21, 2025 - [Workflow Engine 20.0.3](https://workflowengine.io/documentation/release-notes/20.0.3): Release date: December 2, 2025 - [Workflow Engine 20.0.4](https://workflowengine.io/documentation/release-notes/20.0.4): Release date: December 29, 2025 - [Workflow Engine 20.0.5](https://workflowengine.io/documentation/release-notes/20.0.5): Release date: February 6, 2026 - [Workflow Engine 20.0.6](https://workflowengine.io/documentation/release-notes/20.0.6): Release date: February 24, 2026 - [Workflow Engine 20.0.7](https://workflowengine.io/documentation/release-notes/20.0.7): Release date: March 19, 2026 - [Workflow Engine 20.0.8](https://workflowengine.io/documentation/release-notes/20.0.8): Release date: April 1, 2026 - [Workflow Engine 20.0.9](https://workflowengine.io/documentation/release-notes/20.0.9): Release date: April 16, 2026 - [Workflow Engine 21.0.0](https://workflowengine.io/documentation/release-notes/21.0.0): Release date: March 20, 2026 - [Workflow Engine 21.1.0](https://workflowengine.io/documentation/release-notes/21.1.0): Release date: May 19, 2026 - [Workflow Engine 21.1.1](https://workflowengine.io/documentation/release-notes/21.1.1): Release date: May 25, 2026 - [Workflow Engine 3.0](https://workflowengine.io/documentation/release-notes/3.0): Workflow Engine 3.0 release notes - [Workflow Engine 3.1](https://workflowengine.io/documentation/release-notes/3.1): Release date: June 6, 2018 - [Workflow Engine 3.2](https://workflowengine.io/documentation/release-notes/3.2): Release date: August 17, 2018 - [Workflow Engine 3.3](https://workflowengine.io/documentation/release-notes/3.3): Release date: September 12, 2018 - [Workflow Engine 3.4](https://workflowengine.io/documentation/release-notes/3.4): Release date: November 5, 2018 - [Workflow Engine 3.5](https://workflowengine.io/documentation/release-notes/3.5): Release date: January 16, 2019 - [Workflow Engine 4.0](https://workflowengine.io/documentation/release-notes/4.0): Release date: April 25, 2019 - [Workflow Engine 4.1](https://workflowengine.io/documentation/release-notes/4.1): Release date: January 9, 2020 - [Workflow Engine 4.2](https://workflowengine.io/documentation/release-notes/4.2): Release date: May 18, 2020 - [Workflow Engine 5.0](https://workflowengine.io/documentation/release-notes/5.0): Release date: October 7, 2020 - [Workflow Engine 5.1](https://workflowengine.io/documentation/release-notes/5.1): Release date: March 31, 2021 - [Workflow Engine 5.2](https://workflowengine.io/documentation/release-notes/5.2): Release date: October 27, 2021 - [Workflow Engine 5.2.1](https://workflowengine.io/documentation/release-notes/5.2.1): Release date: December 8, 2021 - [Workflow Engine 5.2.2](https://workflowengine.io/documentation/release-notes/5.2.2): Release date: December 29, 2021 - [Workflow Engine 5.2.3](https://workflowengine.io/documentation/release-notes/5.2.3): Release date: March 2, 2022 - [Workflow Engine 5.2.4](https://workflowengine.io/documentation/release-notes/5.2.4): Release date: April 1, 2022 - [Workflow Engine 5.2.5](https://workflowengine.io/documentation/release-notes/5.2.5): Release date: April 4, 2022 - [Workflow Engine 5.2.6](https://workflowengine.io/documentation/release-notes/5.2.6): Release date: April 11, 2022 - [Workflow Engine 5.3.0](https://workflowengine.io/documentation/release-notes/5.3.0): Release date: April 19, 2022 - [Workflow Engine 5.3.1](https://workflowengine.io/documentation/release-notes/5.3.1): Release date: April 25, 2022 - [Workflow Engine 5.3.2](https://workflowengine.io/documentation/release-notes/5.3.2): Release date: April 29, 2022 - [Workflow Engine 5.3.3](https://workflowengine.io/documentation/release-notes/5.3.3): Release date: May 13, 2022 - [Workflow Engine 5.3.4](https://workflowengine.io/documentation/release-notes/5.3.4): Release date: May 23, 2022 - [Workflow Engine 5.3.5](https://workflowengine.io/documentation/release-notes/5.3.5): Release date: May 31, 2022 - [Workflow Engine 6.0.0](https://workflowengine.io/documentation/release-notes/6.0.0): Release date: June 22, 2022 - [Workflow Engine 6.0.1](https://workflowengine.io/documentation/release-notes/6.0.1): Release date: June 24, 2022 - [Workflow Engine 6.0.2](https://workflowengine.io/documentation/release-notes/6.0.2): Release date: July 6, 2022 - [Workflow Engine 7.0.0](https://workflowengine.io/documentation/release-notes/7.0.0): Release date: August 29, 2022 - [Workflow Engine 7.1.0](https://workflowengine.io/documentation/release-notes/7.1.0): Release date: September 16, 2022 - [Workflow Engine 7.1.1](https://workflowengine.io/documentation/release-notes/7.1.1): Release date: September 22, 2022 - [Workflow Engine 7.1.2](https://workflowengine.io/documentation/release-notes/7.1.2): Release date: October 5, 2022 - [Workflow Engine 7.1.3](https://workflowengine.io/documentation/release-notes/7.1.3): Release date: October 7, 2022 - [Workflow Engine 7.1.4](https://workflowengine.io/documentation/release-notes/7.1.4): Release date: October 12, 2022 - [Workflow Engine 7.2.0](https://workflowengine.io/documentation/release-notes/7.2.0): Release date: October 24, 2022 - [Workflow Engine 7.2.1](https://workflowengine.io/documentation/release-notes/7.2.1): Release date: November 7, 2022 - [Workflow Engine 7.2.2](https://workflowengine.io/documentation/release-notes/7.2.2): Release date: November 9, 2022 - [Workflow Engine 7.2.3](https://workflowengine.io/documentation/release-notes/7.2.3): Release date: December 5, 2022 - [Workflow Engine 7.3.0](https://workflowengine.io/documentation/release-notes/7.3.0): Release date: February 13, 2023 - [Workflow Engine 8.0.0](https://workflowengine.io/documentation/release-notes/8.0.0): Release date: February 21, 2023 - [Workflow Engine 8.1.0](https://workflowengine.io/documentation/release-notes/8.1.0): Release date: March 1, 2023 - [Workflow Engine 9.0.0](https://workflowengine.io/documentation/release-notes/9.0.0): Release date: April 20, 2023 - [Workflow Engine 9.0.1](https://workflowengine.io/documentation/release-notes/9.0.1): Release date: May 03, 2023 - [Workflow Engine 9.0.2](https://workflowengine.io/documentation/release-notes/9.0.2): Release date: May 10, 2023 - [Workflow Engine 9.1.0](https://workflowengine.io/documentation/release-notes/9.1.0): Release date: May 24, 2023 - [Workflow Engine 9.1.1](https://workflowengine.io/documentation/release-notes/9.1.1): Release date: June 2, 2023 ### roadmap Get acquainted with the upcoming changes to Workflow Engine and Workflow Server in further releases. - [Roadmap](https://workflowengine.io/documentation/roadmap): Get acquainted with the upcoming changes to Workflow Engine and Workflow Server in further releases. ### sample-action-signalr In this guide we describe how to implement SignalR library in React sample - [Action interaction with SignalR](https://workflowengine.io/documentation/sample-action-signalr): In this guide we describe how to implement SignalR library in React sample ### sample-conditional-branches In this tutorial we describe how conditional transitions work in WFE - [Conditional branches](https://workflowengine.io/documentation/sample-conditional-branches): In this tutorial we describe how conditional transitions work in WFE ### sample-dependency-injection In this tutorial we describe how to implement Dependency injection pattern - [Dependency injection](https://workflowengine.io/documentation/sample-dependency-injection): In this tutorial we describe how to implement Dependency injection pattern ### sample-error-handling In this tutorial we describe how exceptions can be handled during process execution - [Error handling](https://workflowengine.io/documentation/sample-error-handling): In this tutorial we describe how exceptions can be handled during process execution ### scalability This section is fully dedicated to the Workflow Engine scalability and deployment. - [Horizontal Scalability and Multi-Server Mode](https://workflowengine.io/documentation/scalability): This section is fully dedicated to the Workflow Engine scalability and deployment. - [Multi-Server Mode (Horizontal Scalability)](https://workflowengine.io/documentation/scalability/multiserver): Multi-server mode description - [Multitenancy](https://workflowengine.io/documentation/scalability/multitenancy): Full fledged multitenancy implementation is available in Workflow Engine Web API. Read more about it here. - [Failover and Its Customization](https://workflowengine.io/documentation/scalability/restore): Default Recovery - [Single-Server Mode](https://workflowengine.io/documentation/scalability/singleserver): In the single-server mode, a single instance of the Workflow Engine works with a single database. Multiple instances can be launched at the ### scheme This section dwells on the objects a process scheme comprises, as well as the major methods of working with the process. The key scheme - [Scheme in a nutshell](https://workflowengine.io/documentation/scheme): This section dwells on the objects a process scheme comprises, as well as the major methods of working with the process. The key scheme - [Actions, IWorkflowActionProvider and CodeActions](https://workflowengine.io/documentation/scheme/actions): Learn more about Actions, a powerful Workflow engine tool of integrating into an existing application. - [Activities](https://workflowengine.io/documentation/scheme/activities): Activity is the first key object a Workflow Engine scheme comprises, determining the order, in which Actions are executed in your process. - [Annotations / Custom fields](https://workflowengine.io/documentation/scheme/annotations): Annotations are a set of records, a key (or name) - a value you can add to your Activities and Transitions in the designer. - [Commands](https://workflowengine.io/documentation/scheme/commands): Command is one of the external triggers that make a Workflow Engine process leave the idle state and perform a certain action. - [Conditional Transitions. Conditions and Expressions](https://workflowengine.io/documentation/scheme/conditions): General information - [Localization](https://workflowengine.io/documentation/scheme/localization): Workflow Engine supports localization out of the box. Localization constants are stored in the Localization section, they can be accessed by - [Process Parameters](https://workflowengine.io/documentation/scheme/parameters): Parameters are objects that are stored together with a process, and may provide for the transition logic or be used as process artifacts. - [Process logs in Workflow Engine](https://workflowengine.io/documentation/scheme/process-logs): Describing logging functionality in Workflow Engine. - [Programmatic schema builder](https://workflowengine.io/documentation/scheme/programmatic-schema-builder): Building process schemas using code - [Rules, Actors and integration with your security system](https://workflowengine.io/documentation/scheme/rules): Rules serve as a starting point for security integration. They allow you to call the available authorization methods, or write the new ones. - [Timers](https://workflowengine.io/documentation/scheme/timers): Timer is one of the internal triggers that makes a process leave the idle state and perform a certain action. - [Transition](https://workflowengine.io/documentation/scheme/transitions): Transition always connects two Activities together, and controls the sequence of execution of your processes. - [Work Calendars](https://workflowengine.io/documentation/scheme/working-calendars): How to set up and connect working calendars ### states-and-activities In this tutorial we describe how to implement set state, set activity and resume methods - [States and Activities](https://workflowengine.io/documentation/states-and-activities): In this tutorial we describe how to implement set state, set activity and resume methods ### tutorials Workflow Engine Tutorials - [Workflow Engine Tutorials](https://workflowengine.io/documentation/tutorials): Workflow Engine Tutorials ### video-tutorials Before you proceed with the documentation we suggest you get acquainted with video tutorials that will help you - [Video Tutorials](https://workflowengine.io/documentation/video-tutorials): Before you proceed with the documentation we suggest you get acquainted with video tutorials that will help you ### web-api The Workflow Engine Web API is an ASP.NET library that enables seamless integration of the Workflow Engine into your web - [Web API Setup](https://workflowengine.io/documentation/web-api): The Workflow Engine Web API is an ASP.NET library that enables seamless integration of the Workflow Engine into your web - [Core Services](https://workflowengine.io/documentation/web-api/core-services): The Web API core services manage other services and build the API by using an internal builder based on ASP.NET Minimal - [Database Provider](https://workflowengine.io/documentation/web-api/database-provider): Data provider services in the Web API implement operations defined by the core services. There are currently six - [Multitenancy](https://workflowengine.io/documentation/web-api/multitenancy): In Quick Start we covered how to configure the Web API in single-tenant mode. In that mode, the API works - [Open API](https://workflowengine.io/documentation/web-api/open-api) - [Security Services](https://workflowengine.io/documentation/web-api/security-services): Security services in the Web API add authentication and authorization to Workflow API operations. They use standard ### workflow-designer-in-blazor-application In this article, we will look at an example of integrating Workflow Designer into an application on Blazor. - [Workflow Designer in Blazor application](https://workflowengine.io/documentation/workflow-designer-in-blazor-application): In this article, we will look at an example of integrating Workflow Designer into an application on Blazor. ### workflowengine What is Workflow Engine? - [Workflow Engine](https://workflowengine.io/documentation/workflowengine): What is Workflow Engine? --- # Full Documentation Content [Skip to main content](#__docusaurus_skipToContent_fallback) [Introducing Formengine - The New Formbuilder, try for FREE formengine.io.](https://formengine.io/?utm_source=workflowengine\&utm_medium=topbanner) [![WorkflowEngine Logo](/documentation/img/logo.svg)![WorkflowEngine Logo](/documentation/img/logo.svg)](https://workflowengine.io/) [Discuss on GitHub](https://github.com/optimajet/WorkflowEngine.NET/discussions)[Features](https://workflowengine.io/features/)[Benefits](https://workflowengine.io/benefits/)[Demo](https://demo.workflowengine.io/designer)[Server](https://workflowengine.io/server/)[Documentation](https://workflowengine.io/documentation/)[Downloads](https://workflowengine.io/downloads/net-core/)[Pricing](https://workflowengine.io/pricing/)[Blog](https://workflowengine.io/blog/)[Contact](https://workflowengine.io/contacts/) Search Search... * Approvals * delDelete a single approval history entry for a specific process instance ID and entry ID. * getRetrieve a single approval history entry for a specific process instance ID and entry ID. * delDelete multiple approval history entries for a specific process instance ID, with optional filters. * getRetrieve approval history entries collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. * postRetrieve approval history entries collection and total count across all process instances, with optional search, filters, sorting, and paging. * Designer * postworkflow-api.designer * getworkflow-api.designer.get * GlobalParameters * postCreate multiple global parameters from an array of creation requests. * getRetrieve global parameters collection and total count, with optional search, filters, sorting, and paging. * delDelete multiple global parameters based on optional filters. * postCreate a single global parameter using type, name and creation request. * getRetrieve a single global parameter by type and name. * delDelete a single global parameter by type and name. * putUpdate a single global parameter by type and name, using an update request. * postRetrieve global parameters collection and total count, with optional search, filters, sorting, and paging. * InboxEntries * delDelete multiple inbox entries for a specific process instance ID, with optional filters. * getRetrieve inbox entries collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. * getRetrieve a single inbox entry for a specific process instance ID and identity ID. * delDelete a single inbox entry for a specific process instance ID and identity ID. * postRetrieve inbox entries collection and total count across all process instances, with optional search, filters, sorting, and paging. * Parameters * delDelete a single parameter for a specific process instance ID and parameter name. * putUpdate a single parameter for a specific process instance ID and parameter name, using an update request. * getRetrieve a single parameter for a specific process instance ID and parameter name. * postCreate a single parameter for a specific process instance ID using parameter name and creation request. * getRetrieve parameters collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. * delDelete multiple parameters for a specific process instance ID, with optional filters. * postCreate multiple parameters for a specific process instance ID from an array of creation requests. * postRetrieve parameters collection and total count across all process instances, with optional search, filters, sorting, and paging. * Processes * putUpdate a single process by ID. * getRetrieve a single process by ID. * postRetrieve processes collection and total count, with optional search, filters, sorting, and paging. * getRetrieve processes collection and total count, with optional search, filters, sorting, and paging. * Root * getCheck all tenants is ready to accept requests * getCheck specific tenant is ready to accept requests * getCheck service is running * RpcBulk * postCreate multiple process instances. * postGet multiple root process instances with all subprocesses as trees. * postGet multiple process instances with parameters. * postUpdate multiple schemes of process instances if they are obsolete. * postExecute multiple commands with specified identity. * postGet multiple lists of commands available for current activities and identities. * postCheck existence of multiple process instances. * postDelete multiple process instances with all their subprocesses. * RpcCommands * postGet the list of commands available for current activity and identities. * postGet the list of commands available for initial activity and identities. * postExecute command with specified identity. * RpcInstance * postGet the root process instance and all subprocesses as a tree. * postGet the value of a process parameter. * postDelete process instance and all child subprocesses. * postSet a new status for the process instance. * postCheck if all subprocesses of the process instance are completed. * postGet the status of the process instance. * postGet the process instance with all parameters. * postDelete all subprocesses of the process instance. * postSet the value of a process parameter with persistence purpose. * postGet the history records for the process instance. * postCreate instance of the process. * postCheck existence of the process instance. * postGet the count of history records for the process instance. * RpcLog * postLogs a debug message with parameters. * postLogs a debug message with parameters if the logger exists. * postLogs an error message with parameters if the logger exists. * postLogs an error message with parameters. * postLogs an info message with parameters if the logger exists. * postLogs an info message with parameters. * RpcPreExecution * postPre-execute from the initial activity of the process instance. * postPre-execute from the current activity of the process instance. * postPre-execute from the specified activity of the process instance. * RpcRuntime * postStarts the workflow runtime in cold start mode. * postGets the status of the workflow runtime. * postShuts down the workflow runtime. * postStarts the workflow runtime. * RpcScheme * postUpdate scheme of the process instance if it is obsolete. * postGet the scheme of the process instance. * postGet scheme codes with optional filtering by tags. * postSet all process schemes with the specified scheme code as obsolete. * RpcState * postGet the current state name of the process instance. * postSet activity for the process instance without execution. * postSet state for the process instance with execution. * postGet the current state of the process instance. * postSet activity for the process instance with execution. * postGet the list of all available to set states for the process instance. * postSet state for the process instance without execution. * postResumes the process instance execution. * postGet the list of all available to set states for the scheme. * postGet the initial state of the scheme. * postGet the current activity name of the process instance. * Runtimes * getRetrieve a single runtime by ID. * getRetrieve runtimes collection and total count, with optional search, filters, sorting, and paging. * postRetrieve runtimes collection and total count, with optional search, filters, sorting, and paging. * Schemes * getRetrieve a single scheme by code. * postCreate a single scheme using code and creation request. * putUpdate a single scheme by code, using an update request. * delDelete a single scheme by code. * getRetrieve schemes collection and total count, with optional search, filters, sorting, and paging. * delDelete multiple schemes based on optional filters. * postCreate multiple schemes from an array of creation requests. * postRetrieve schemes collection and total count, with optional search, filters, sorting, and paging. * Statuses * getRetrieve process statuses collection and total count, with optional search, filters, sorting, and paging. * getRetrieve a single process status by process ID. * postRetrieve process statuses collection and total count, with optional search, filters, sorting, and paging. * Timers * postRetrieve timers collection and total count across all process instances, with optional search, filters, sorting, and paging. * putUpdate a single timer for a specific process instance ID and timer name, using an update request. * getRetrieve a single timer for a specific process instance ID and timer name. * delDelete a single timer for a specific process instance ID and timer name. * postCreate a single timer for a specific process instance ID, using timer name and creation request. * delDelete multiple timers for a specific process instance ID, with optional filters. * getRetrieve timers collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. * postCreate multiple timers for a specific process instance ID from an array of creation requests. * Transitions * delDelete multiple transitions for a specific process instance ID, with optional filters. * getRetrieve transitions collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. * getRetrieve a single transition for a specific process instance ID and transition ID. * delDelete a single transition for a specific process instance ID and transition ID. * postRetrieve transitions collection and total count across all process instances, with optional search, filters, sorting, and paging. [API docs by Redocly](https://redocly.com/redoc/) # Workflow Engine API (1.0) Download OpenAPI specification:[Download](https://workflowengine.io/documentation/redocusaurus/api.yaml) A Workflow Engine Web API ## [](#tag/Approvals)Approvals ## [](#tag/Approvals/operation/workflow-api.data.processes.approvals.delete)Delete a single approval history entry for a specific process instance ID and entry ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | idrequired | string<uuid> | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/approvals/{id} https\://workflowengine.io/workflow-api/data/processes/{processId}/approvals/{id} ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Approvals/operation/workflow-api.data.processes.approvals.get)Retrieve a single approval history entry for a specific process instance ID and entry ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | idrequired | string<uuid> | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/processes/{processId}/approvals/{id} https\://workflowengine.io/workflow-api/data/processes/{processId}/approvals/{id} ### Response samples * 200 * 400 * 404 Content type application/json Copy Expand all Collapse all `{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityId": "string", "allowedTo": [ "string" ], "transitionTime": "2019-08-24T14:15:22Z", "sort": 0, "initialState": "string", "destinationState": "string", "triggerName": "string", "commentary": "string" }` ## [](#tag/Approvals/operation/workflow-api.data.processes.approvals.delete-collection)Delete multiple approval history entries for a specific process instance ID, with optional filters. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | ------------------------------------------------------ | | filters | Array of objects (ApprovalFieldFilter) | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/approvals https\://workflowengine.io/workflow-api/data/processes/{processId}/approvals ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Approvals/operation/workflow-api.data.processes.approvals.get-collection)Retrieve approval history entries collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (ApprovalFieldFilter) | | sorts | Array of objects (ApprovalFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/processes/{processId}/approvals https\://workflowengine.io/workflow-api/data/processes/{processId}/approvals ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityId": "string", "allowedTo": [ "string" ], "transitionTime": "2019-08-24T14:15:22Z", "sort": 0, "initialState": "string", "destinationState": "string", "triggerName": "string", "commentary": "string" } ], "total": 0 }` ## [](#tag/Approvals/operation/workflow-api.search.processes.approvals)Retrieve approval history entries collection and total count across all process instances, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | -------------------------------------------------------------- | | search | string or null | | filters | Array of objects or null (ApprovalFieldFilter) | | sorts | Array of objects or null (ApprovalFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/processes/approvals https\://workflowengine.io/workflow-api/search/processes/approvals ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "Id", "value": null } ], "sorts": [ { "field": "Id", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityId": "string", "allowedTo": [ "string" ], "transitionTime": "2019-08-24T14:15:22Z", "sort": 0, "initialState": "string", "destinationState": "string", "triggerName": "string", "commentary": "string" } ], "total": 0 }` ## [](#tag/Designer)Designer ## [](#tag/Designer/operation/workflow-api.designer)workflow-api.designer ##### Authorizations: *Bearer* ### Responses **200** OK post/workflow-api/designer https\://workflowengine.io/workflow-api/designer ## [](#tag/Designer/operation/workflow-api.designer.get)workflow-api.designer.get ##### Authorizations: *Bearer* ### Responses **200** OK get/workflow-api/designer https\://workflowengine.io/workflow-api/designer ## [](#tag/GlobalParameters)GlobalParameters ## [](#tag/GlobalParameters/operation/workflow-api.data.global-parameters.create-collection)Create multiple global parameters from an array of creation requests. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired Array | | | | ----- | -------------- | | type | string or null | | name | string or null | | value | any or null | ### Responses **200** OK **400** Bad Request post/workflow-api/data/global-parameters https\://workflowengine.io/workflow-api/data/global-parameters ### Request samples * Payload Content type application/json Copy Expand all Collapse all `[ { "type": "string", "name": "string", "value": null } ]` ### Response samples * 200 * 400 Content type application/json Copy `{ "createdCount": 0 }` ## [](#tag/GlobalParameters/operation/workflow-api.data.global-parameters.get-collection)Retrieve global parameters collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (GlobalParameterFieldFilter) | | sorts | Array of objects (GlobalParameterFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/global-parameters https\://workflowengine.io/workflow-api/data/global-parameters ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "type": "string", "name": "string", "value": null } ], "total": 0 }` ## [](#tag/GlobalParameters/operation/workflow-api.data.global-parameters.delete-collection)Delete multiple global parameters based on optional filters. ##### Authorizations: *Bearer* ##### query Parameters | | | | ------- | ------------------------------------------------------------- | | filters | Array of objects (GlobalParameterFieldFilter) | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/global-parameters https\://workflowengine.io/workflow-api/data/global-parameters ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/GlobalParameters/operation/workflow-api.data.global-parameters.create)Create a single global parameter using type, name and creation request. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------ | ------ | | typerequired | string | | namerequired | string | ##### Request Body schema: application/jsonrequired | | | | ----- | ----------- | | value | any or null | ### Responses **200** OK **400** Bad Request post/workflow-api/data/global-parameters/{type}/{name} https\://workflowengine.io/workflow-api/data/global-parameters/{type}/{name} ### Request samples * Payload Content type application/json Copy `{ "value": null }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/GlobalParameters/operation/workflow-api.data.global-parameters.get)Retrieve a single global parameter by type and name. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------ | ------ | | typerequired | string | | namerequired | string | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/global-parameters/{type}/{name} https\://workflowengine.io/workflow-api/data/global-parameters/{type}/{name} ### Response samples * 200 * 400 * 404 Content type application/json Copy `{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "type": "string", "name": "string", "value": null }` ## [](#tag/GlobalParameters/operation/workflow-api.data.global-parameters.delete)Delete a single global parameter by type and name. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------ | ------ | | typerequired | string | | namerequired | string | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/global-parameters/{type}/{name} https\://workflowengine.io/workflow-api/data/global-parameters/{type}/{name} ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/GlobalParameters/operation/workflow-api.data.global-parameters.update)Update a single global parameter by type and name, using an update request. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------ | ------ | | typerequired | string | | namerequired | string | ##### Request Body schema: application/jsonrequired | | | | ----- | -------------- | | type | string or null | | name | string or null | | value | any or null | ### Responses **200** OK **400** Bad Request put/workflow-api/data/global-parameters/{type}/{name} https\://workflowengine.io/workflow-api/data/global-parameters/{type}/{name} ### Request samples * Payload Content type application/json Copy `{ "type": "string", "name": "string", "value": null }` ### Response samples * 200 * 400 Content type application/json Copy `{ "updatedCount": 0 }` ## [](#tag/GlobalParameters/operation/workflow-api.search.global-parameters)Retrieve global parameters collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | --------------------------------------------------------------------- | | search | string or null | | filters | Array of objects or null (GlobalParameterFieldFilter) | | sorts | Array of objects or null (GlobalParameterFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/global-parameters https\://workflowengine.io/workflow-api/search/global-parameters ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "Id", "value": null } ], "sorts": [ { "field": "Id", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "type": "string", "name": "string", "value": null } ], "total": 0 }` ## [](#tag/InboxEntries)InboxEntries ## [](#tag/InboxEntries/operation/workflow-api.data.processes.inbox-entries.delete-collection)Delete multiple inbox entries for a specific process instance ID, with optional filters. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | -------------------------------------------------------- | | filters | Array of objects (InboxEntryFieldFilter) | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/inbox-entries https\://workflowengine.io/workflow-api/data/processes/{processId}/inbox-entries ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/InboxEntries/operation/workflow-api.data.processes.inbox-entries.get-collection)Retrieve inbox entries collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (InboxEntryFieldFilter) | | sorts | Array of objects (InboxEntryFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/processes/{processId}/inbox-entries https\://workflowengine.io/workflow-api/data/processes/{processId}/inbox-entries ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityId": "string", "addingDate": "2019-08-24T14:15:22Z", "availableCommands": [ "string" ] } ], "total": 0 }` ## [](#tag/InboxEntries/operation/workflow-api.data.processes.inbox-entries.get)Retrieve a single inbox entry for a specific process instance ID and identity ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------------ | -------------------------------------------- | | processIdrequired | string<uuid> | | identityIdrequired | string | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/processes/{processId}/inbox-entries/{identityId} https\://workflowengine.io/workflow-api/data/processes/{processId}/inbox-entries/{identityId} ### Response samples * 200 * 400 * 404 Content type application/json Copy Expand all Collapse all `{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityId": "string", "addingDate": "2019-08-24T14:15:22Z", "availableCommands": [ "string" ] }` ## [](#tag/InboxEntries/operation/workflow-api.data.processes.inbox-entries.delete)Delete a single inbox entry for a specific process instance ID and identity ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------------ | -------------------------------------------- | | processIdrequired | string<uuid> | | identityIdrequired | string | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/inbox-entries/{identityId} https\://workflowengine.io/workflow-api/data/processes/{processId}/inbox-entries/{identityId} ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/InboxEntries/operation/workflow-api.search.processes.inbox-entries)Retrieve inbox entries collection and total count across all process instances, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | ---------------------------------------------------------------- | | search | string or null | | filters | Array of objects or null (InboxEntryFieldFilter) | | sorts | Array of objects or null (InboxEntryFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/processes/inbox-entries https\://workflowengine.io/workflow-api/search/processes/inbox-entries ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "Id", "value": null } ], "sorts": [ { "field": "Id", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityId": "string", "addingDate": "2019-08-24T14:15:22Z", "availableCommands": [ "string" ] } ], "total": 0 }` ## [](#tag/Parameters)Parameters ## [](#tag/Parameters/operation/workflow-api.data.processes.parameters.delete)Delete a single parameter for a specific process instance ID and parameter name. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | namerequired | string | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/parameters/{name} https\://workflowengine.io/workflow-api/data/processes/{processId}/parameters/{name} ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Parameters/operation/workflow-api.data.processes.parameters.update)Update a single parameter for a specific process instance ID and parameter name, using an update request. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | namerequired | string | ##### Request Body schema: application/jsonrequired | | | | ----- | -------------- | | name | string or null | | value | any or null | ### Responses **200** OK **400** Bad Request put/workflow-api/data/processes/{processId}/parameters/{name} https\://workflowengine.io/workflow-api/data/processes/{processId}/parameters/{name} ### Request samples * Payload Content type application/json Copy `{ "name": "string", "value": null }` ### Response samples * 200 * 400 Content type application/json Copy `{ "updatedCount": 0 }` ## [](#tag/Parameters/operation/workflow-api.data.processes.parameters.get)Retrieve a single parameter for a specific process instance ID and parameter name. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | namerequired | string | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/processes/{processId}/parameters/{name} https\://workflowengine.io/workflow-api/data/processes/{processId}/parameters/{name} ### Response samples * 200 * 400 * 404 Content type application/json Copy `{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "value": null }` ## [](#tag/Parameters/operation/workflow-api.data.processes.parameters.create)Create a single parameter for a specific process instance ID using parameter name and creation request. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | namerequired | string | ##### Request Body schema: application/jsonrequired | | | | ----- | ----------- | | value | any or null | ### Responses **200** OK **400** Bad Request post/workflow-api/data/processes/{processId}/parameters/{name} https\://workflowengine.io/workflow-api/data/processes/{processId}/parameters/{name} ### Request samples * Payload Content type application/json Copy `{ "value": null }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/Parameters/operation/workflow-api.data.processes.parameters.get-collection)Retrieve parameters collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (ParameterFieldFilter) | | sorts | Array of objects (ParameterFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/processes/{processId}/parameters https\://workflowengine.io/workflow-api/data/processes/{processId}/parameters ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "value": null } ], "total": 0 }` ## [](#tag/Parameters/operation/workflow-api.data.processes.parameters.delete-collection)Delete multiple parameters for a specific process instance ID, with optional filters. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | ------------------------------------------------------- | | filters | Array of objects (ParameterFieldFilter) | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/parameters https\://workflowengine.io/workflow-api/data/processes/{processId}/parameters ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Parameters/operation/workflow-api.data.processes.parameters.create-collection)Create multiple parameters for a specific process instance ID from an array of creation requests. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### Request Body schema: application/jsonrequired Array | | | | ----- | -------------- | | name | string or null | | value | any or null | ### Responses **200** OK **400** Bad Request post/workflow-api/data/processes/{processId}/parameters https\://workflowengine.io/workflow-api/data/processes/{processId}/parameters ### Request samples * Payload Content type application/json Copy Expand all Collapse all `[ { "name": "string", "value": null } ]` ### Response samples * 200 * 400 Content type application/json Copy `{ "createdCount": 0 }` ## [](#tag/Parameters/operation/workflow-api.search.processes.parameters)Retrieve parameters collection and total count across all process instances, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | --------------------------------------------------------------- | | search | string or null | | filters | Array of objects or null (ParameterFieldFilter) | | sorts | Array of objects or null (ParameterFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/processes/parameters https\://workflowengine.io/workflow-api/search/processes/parameters ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "Id", "value": null } ], "sorts": [ { "field": "Id", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "value": null } ], "total": 0 }` ## [](#tag/Processes)Processes ## [](#tag/Processes/operation/workflow-api.data.processes.update)Update a single process by ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ---------- | -------------------------------------------- | | idrequired | string<uuid> | ##### Request Body schema: application/jsonrequired | | | | ------------ | -------------- | | tenantId | string or null | | calendarName | string or null | ### Responses **200** OK **400** Bad Request put/workflow-api/data/processes/{id} https\://workflowengine.io/workflow-api/data/processes/{id} ### Request samples * Payload Content type application/json Copy `{ "tenantId": "string", "calendarName": "string" }` ### Response samples * 200 * 400 Content type application/json Copy `{ "updatedCount": 0 }` ## [](#tag/Processes/operation/workflow-api.data.processes.get)Retrieve a single process by ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ---------- | -------------------------------------------- | | idrequired | string<uuid> | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/processes/{id} https\://workflowengine.io/workflow-api/data/processes/{id} ### Response samples * 200 * 400 * 404 Content type application/json Copy `{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "stateName": "string", "activityName": "string", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivity": "string", "previousActivityForDirect": "string", "previousActivityForReverse": "string", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "tenantId": "string", "startingTransition": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "calendarName": "string" }` ## [](#tag/Processes/operation/workflow-api.search.processes)Retrieve processes collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | ------------------------------------------------------------- | | search | string or null | | filters | Array of objects or null (ProcessFieldFilter) | | sorts | Array of objects or null (ProcessFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/processes https\://workflowengine.io/workflow-api/search/processes ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "Id", "value": null } ], "sorts": [ { "field": "Id", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "stateName": "string", "activityName": "string", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivity": "string", "previousActivityForDirect": "string", "previousActivityForReverse": "string", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "tenantId": "string", "startingTransition": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "calendarName": "string" } ], "total": 0 }` ## [](#tag/Processes/operation/workflow-api.data.processes.get-collection)Retrieve processes collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (ProcessFieldFilter) | | sorts | Array of objects (ProcessFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/processes https\://workflowengine.io/workflow-api/data/processes ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "stateName": "string", "activityName": "string", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivity": "string", "previousActivityForDirect": "string", "previousActivityForReverse": "string", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "tenantId": "string", "startingTransition": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "calendarName": "string" } ], "total": 0 }` ## [](#tag/Root)Root ## [](#tag/Root/operation/workflow-api.readiness)Check all tenants is ready to accept requests ##### Authorizations: *Bearer* ### Responses **200** OK **400** Bad Request get/workflow-api/readiness https\://workflowengine.io/workflow-api/readiness ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "isHealthy": true, "message": "string", "tenantStatuses": [ { "tenantId": "string", "isHealthy": true, "databaseStatus": "Connected", "databaseException": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "workflowRuntimeRunningStatus": "Stopped", "workflowRuntimeException": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } } } ] }` ## [](#tag/Root/operation/workflow-api.tenant-readiness)Check specific tenant is ready to accept requests ##### Authorizations: *Bearer* ### Responses **200** OK **400** Bad Request get/workflow-api/tenant-readiness https\://workflowengine.io/workflow-api/tenant-readiness ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "tenantStatus": { "tenantId": "string", "isHealthy": true, "databaseStatus": "Connected", "databaseException": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "workflowRuntimeRunningStatus": "Stopped", "workflowRuntimeException": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } } } }` ## [](#tag/Root/operation/workflow-api.liveness)Check service is running ##### Authorizations: *Bearer* ### Responses **200** OK **400** Bad Request get/workflow-api/liveness https\://workflowengine.io/workflow-api/liveness ### Response samples * 400 Content type application/json Copy `{ "code": "Unknown", "message": "string" }` ## [](#tag/RpcBulk)RpcBulk ## [](#tag/RpcBulk/operation/workflow-api.rpc.bulk-create-instance)Create multiple process instances. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------------------------ | ------------------------------------------------------------------- | | createInstanceParamsList | Array of objects or null (CreateInstanceParamsItem) | | maxDegreeOfParallelism | integer or null<int32> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/bulk-create-instance https\://workflowengine.io/workflow-api/rpc/bulk-create-instance ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "createInstanceParamsList": [ { "schemeCode": "string", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityId": "string", "impersonatedIdentityId": "string", "calendarName": "string", "initialProcessParameters": [ { "name": "string", "value": null, "purpose": "Temporary" } ] } ], "maxDegreeOfParallelism": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "property1": { "state": "Created", "result": null, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" }, "property2": { "state": "Created", "result": null, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" } }` ## [](#tag/RpcBulk/operation/workflow-api.rpc.bulk-get-process-instances-tree)Get multiple root process instances with all subprocesses as trees. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ----------------------------------------------------------------------------------------------- | | rootProcessIds | Array of strings or null<uuid>\[ items <uuid> ] | | maxDegreeOfParallelism | integer or null<int32> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/bulk-get-process-instance-tree https\://workflowengine.io/workflow-api/rpc/bulk-get-process-instance-tree ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "rootProcessIds": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "maxDegreeOfParallelism": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "property1": { "state": "Created", "result": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "name": "string", "startingTransitionName": "string", "isRoot": true, "children": [ { } ] }, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" }, "property2": { "state": "Created", "result": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "name": "string", "startingTransitionName": "string", "isRoot": true, "children": [ { } ] }, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" } }` ## [](#tag/RpcBulk/operation/workflow-api.rpc.bulk-get-process-instance)Get multiple process instances with parameters. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ----------------------------------------------------------------------------------------------- | | processIds | Array of strings or null<uuid>\[ items <uuid> ] | | maxDegreeOfParallelism | integer or null<int32> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/bulk-get-process-instance https\://workflowengine.io/workflow-api/rpc/bulk-get-process-instance ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processIds": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "maxDegreeOfParallelism": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "property1": { "state": "Created", "result": { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivityName": "string", "previousActivityForDirectName": "string", "previousActivityForReverseName": "string", "currentActivityName": "string", "schemeCode": "string", "currentState": "string", "tenantId": "string", "identityId": "string", "impersonatedIdentityId": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "logEnabled": true, "calendarName": "string", "isSchemeObsolete": true, "isSubprocess": true, "processParameters": [ { "name": "string", "purpose": "Temporary", "type": "string", "value": null, "isImplicit": true } ] }, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" }, "property2": { "state": "Created", "result": { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivityName": "string", "previousActivityForDirectName": "string", "previousActivityForReverseName": "string", "currentActivityName": "string", "schemeCode": "string", "currentState": "string", "tenantId": "string", "identityId": "string", "impersonatedIdentityId": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "logEnabled": true, "calendarName": "string", "isSchemeObsolete": true, "isSubprocess": true, "processParameters": [ { "name": "string", "purpose": "Temporary", "type": "string", "value": null, "isImplicit": true } ] }, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" } }` ## [](#tag/RpcBulk/operation/workflow-api.rpc.bulk-update-scheme-if-obsolete)Update multiple schemes of process instances if they are obsolete. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ----------------------------------------------------------------------------------------------- | | processIds | Array of strings or null<uuid>\[ items <uuid> ] | | ignoreAutoSchemeUpdate | boolean | | maxDegreeOfParallelism | integer or null<int32> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/bulk-update-scheme-if-obsolete https\://workflowengine.io/workflow-api/rpc/bulk-update-scheme-if-obsolete ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processIds": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "ignoreAutoSchemeUpdate": true, "maxDegreeOfParallelism": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "property1": { "state": "Created", "result": null, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" }, "property2": { "state": "Created", "result": null, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" } }` ## [](#tag/RpcBulk/operation/workflow-api.rpc.bulk-execute-command)Execute multiple commands with specified identity. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ---------------------------------------------------------- | | commands | Array of objects or null (WorkflowCommand) | | identityId | string or null | | impersonatedIdentityId | string or null | | checkRestrictions | boolean | | maxDegreeOfParallelism | integer or null<int32> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/bulk-execute-command https\://workflowengine.io/workflow-api/rpc/bulk-execute-command ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "commands": [ { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "localizedName": "string", "parameters": [ { "name": "string", "localizedName": "string", "isRequired": true, "value": null, "defaultValue": null, "typeName": "string", "purpose": "Temporary" } ], "validForActivityName": "string", "validForStateName": "string", "isForSubprocess": true, "classifier": "NotSpecified", "identities": [ "string" ] } ], "identityId": "string", "impersonatedIdentityId": "string", "checkRestrictions": true, "maxDegreeOfParallelism": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "property1": { "state": "Created", "result": { "commandName": "string", "wasExecuted": true, "processInstance": { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivityName": "string", "previousActivityForDirectName": "string", "previousActivityForReverseName": "string", "currentActivityName": "string", "schemeCode": "string", "currentState": "string", "tenantId": "string", "identityId": "string", "impersonatedIdentityId": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "logEnabled": true, "calendarName": "string", "isSchemeObsolete": true, "isSubprocess": true, "processParameters": [ { "name": "string", "purpose": "Temporary", "type": "string", "value": null, "isImplicit": true } ] }, "message": "string" }, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" }, "property2": { "state": "Created", "result": { "commandName": "string", "wasExecuted": true, "processInstance": { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivityName": "string", "previousActivityForDirectName": "string", "previousActivityForReverseName": "string", "currentActivityName": "string", "schemeCode": "string", "currentState": "string", "tenantId": "string", "identityId": "string", "impersonatedIdentityId": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "logEnabled": true, "calendarName": "string", "isSchemeObsolete": true, "isSubprocess": true, "processParameters": [ { "name": "string", "purpose": "Temporary", "type": "string", "value": null, "isImplicit": true } ] }, "message": "string" }, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" } }` ## [](#tag/RpcBulk/operation/workflow-api.rpc.bulk-get-available-commands)Get multiple lists of commands available for current activities and identities. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ----------------------------------------------------------------------------------------------- | | processIds | Array of strings or null<uuid>\[ items <uuid> ] | | identityIds | Array of strings or null | | commandNameFilter | string or null | | culture | string or null | | conditionCheck | boolean | | maxDegreeOfParallelism | integer or null<int32> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/bulk-get-available-commands https\://workflowengine.io/workflow-api/rpc/bulk-get-available-commands ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processIds": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "identityIds": [ "string" ], "commandNameFilter": "string", "culture": "string", "conditionCheck": true, "maxDegreeOfParallelism": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "property1": { "state": "Created", "result": [ { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "localizedName": "string", "parameters": [ { "name": "string", "localizedName": "string", "isRequired": true, "value": null, "defaultValue": null, "typeName": "string", "purpose": "Temporary" } ], "validForActivityName": "string", "validForStateName": "string", "isForSubprocess": true, "classifier": "NotSpecified", "identities": [ "string" ] } ], "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" }, "property2": { "state": "Created", "result": [ { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "localizedName": "string", "parameters": [ { "name": "string", "localizedName": "string", "isRequired": true, "value": null, "defaultValue": null, "typeName": "string", "purpose": "Temporary" } ], "validForActivityName": "string", "validForStateName": "string", "isForSubprocess": true, "classifier": "NotSpecified", "identities": [ "string" ] } ], "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" } }` ## [](#tag/RpcBulk/operation/workflow-api.rpc.bulk-is-process-exists)Check existence of multiple process instances. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ----------------------------------------------------------------------------------------------- | | processIds | Array of strings or null<uuid>\[ items <uuid> ] | | maxDegreeOfParallelism | integer or null<int32> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/bulk-is-process-exists https\://workflowengine.io/workflow-api/rpc/bulk-is-process-exists ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processIds": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "maxDegreeOfParallelism": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "property1": { "state": "Created", "result": true, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" }, "property2": { "state": "Created", "result": true, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" } }` ## [](#tag/RpcBulk/operation/workflow-api.rpc.bulk-delete-instance)Delete multiple process instances with all their subprocesses. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ----------------------------------------------------------------------------------------------- | | processIds | Array of strings or null<uuid>\[ items <uuid> ] | | maxDegreeOfParallelism | integer or null<int32> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/bulk-delete-instance https\://workflowengine.io/workflow-api/rpc/bulk-delete-instance ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processIds": [ "497f6eca-6276-4993-bfeb-53cbbbba6f08" ], "maxDegreeOfParallelism": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "property1": { "state": "Created", "result": null, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" }, "property2": { "state": "Created", "result": null, "exception": { "type": "string", "message": "string", "data": { "property1": null, "property2": null }, "stackTrace": "string", "source": "string", "helpLink": "string", "hResult": 0, "innerException": { } }, "startedAt": "2019-08-24T14:15:22Z", "finalizedAt": "2019-08-24T14:15:22Z", "duration": "string" } }` ## [](#tag/RpcCommands)RpcCommands ## [](#tag/RpcCommands/operation/workflow-api.rpc.get-available-commands)Get the list of commands available for current activity and identities. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ----------------- | -------------------------------------------- | | processId | string<uuid> | | identityIds | Array of strings or null | | commandNameFilter | string or null | | culture | string or null | | conditionCheck | boolean | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-available-commands https\://workflowengine.io/workflow-api/rpc/get-available-commands ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityIds": [ "string" ], "commandNameFilter": "string", "culture": "string", "conditionCheck": true }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "availableCommands": [ { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "localizedName": "string", "parameters": [ { "name": "string", "localizedName": "string", "isRequired": true, "value": null, "defaultValue": null, "typeName": "string", "purpose": "Temporary" } ], "validForActivityName": "string", "validForStateName": "string", "isForSubprocess": true, "classifier": "NotSpecified", "identities": [ "string" ] } ] }` ## [](#tag/RpcCommands/operation/workflow-api.rpc.get-initial-commands)Get the list of commands available for initial activity and identities. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ----------------- | -------------- | | schemeCode | string or null | | commandNameFilter | string or null | | culture | string or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-initial-commands https\://workflowengine.io/workflow-api/rpc/get-initial-commands ### Request samples * Payload Content type application/json Copy `{ "schemeCode": "string", "commandNameFilter": "string", "culture": "string" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "initialCommands": [ { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "localizedName": "string", "parameters": [ { "name": "string", "localizedName": "string", "isRequired": true, "value": null, "defaultValue": null, "typeName": "string", "purpose": "Temporary" } ], "validForActivityName": "string", "validForStateName": "string", "isForSubprocess": true, "classifier": "NotSpecified", "identities": [ "string" ] } ] }` ## [](#tag/RpcCommands/operation/workflow-api.rpc.execute-command)Execute command with specified identity. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ---------------------------------------- | | command | object (WorkflowCommand) | | identityId | string or null | | impersonatedIdentityId | string or null | | checkRestrictions | boolean | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/execute-command https\://workflowengine.io/workflow-api/rpc/execute-command ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "command": { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "localizedName": "string", "parameters": [ { "name": "string", "localizedName": "string", "isRequired": true, "value": null, "defaultValue": null, "typeName": "string", "purpose": "Temporary" } ], "validForActivityName": "string", "validForStateName": "string", "isForSubprocess": true, "classifier": "NotSpecified", "identities": [ "string" ] }, "identityId": "string", "impersonatedIdentityId": "string", "checkRestrictions": true }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "commandName": "string", "wasExecuted": true, "processInstance": { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivityName": "string", "previousActivityForDirectName": "string", "previousActivityForReverseName": "string", "currentActivityName": "string", "schemeCode": "string", "currentState": "string", "tenantId": "string", "identityId": "string", "impersonatedIdentityId": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "logEnabled": true, "calendarName": "string", "isSchemeObsolete": true, "isSubprocess": true, "processParameters": [ { "name": "string", "purpose": "Temporary", "type": "string", "value": null, "isImplicit": true } ] }, "message": "string" }` ## [](#tag/RpcInstance)RpcInstance ## [](#tag/RpcInstance/operation/workflow-api.rpc.get-process-instance-tree)Get the root process instance and all subprocesses as a tree. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-process-instance-tree https\://workflowengine.io/workflow-api/rpc/get-process-instance-tree ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "processInstanceTree": { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "name": "string", "startingTransitionName": "string", "isRoot": true, "children": [ { } ] } }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.get-process-parameter)Get the value of a process parameter. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | | name | string or null | ### Responses **200** OK **400** Bad Request **404** Not Found post/workflow-api/rpc/get-process-parameter https\://workflowengine.io/workflow-api/rpc/get-process-parameter ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string" }` ### Response samples * 200 * 400 * 404 Content type application/json Copy `{ "name": "string", "value": null }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.delete-instance)Delete process instance and all child subprocesses. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/delete-instance https\://workflowengine.io/workflow-api/rpc/delete-instance ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.set-process-new-status)Set a new status for the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | processId | string<uuid> | | processStatus | string (ProcessStatusAvailableToSet)Enum: "Initialized" "Running" "Idled" "Finalized" "Terminated" "Error" | | suppressEvent | boolean | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/set-process-new-status https\://workflowengine.io/workflow-api/rpc/set-process-new-status ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "processStatus": "Initialized", "suppressEvent": true }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.check-all-subprocesses-completed)Check if all subprocesses of the process instance are completed. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/check-all-subprocesses-completed https\://workflowengine.io/workflow-api/rpc/check-all-subprocesses-completed ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy `{ "isAllSubprocessesCompleted": true }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.get-process-status)Get the status of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-process-status https\://workflowengine.io/workflow-api/rpc/get-process-status ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy `{ "processStatus": "Initialized" }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.get-process-instance)Get the process instance with all parameters. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-process-instance https\://workflowengine.io/workflow-api/rpc/get-process-instance ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "processInstance": { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivityName": "string", "previousActivityForDirectName": "string", "previousActivityForReverseName": "string", "currentActivityName": "string", "schemeCode": "string", "currentState": "string", "tenantId": "string", "identityId": "string", "impersonatedIdentityId": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "logEnabled": true, "calendarName": "string", "isSchemeObsolete": true, "isSubprocess": true, "processParameters": [ { "name": "string", "purpose": "Temporary", "type": "string", "value": null, "isImplicit": true } ] } }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.delete-all-subprocesses)Delete all subprocesses of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/delete-all-subprocesses https\://workflowengine.io/workflow-api/rpc/delete-all-subprocesses ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedSubprocessesCount": 0 }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.set-process-parameter)Set the value of a process parameter with persistence purpose. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | | name | string or null | | value | any or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/set-process-parameter https\://workflowengine.io/workflow-api/rpc/set-process-parameter ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "name": "string", "value": null }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.get-process-history)Get the history records for the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | | paging | object (Paging) | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-process-history https\://workflowengine.io/workflow-api/rpc/get-process-history ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "paging": { "pageIndex": 0, "pageSize": 0 } }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "history": [ { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "actorIdentityId": "string", "executorName": "string", "actorName": "string", "executorIdentityId": "string", "fromActivityName": "string", "fromStateName": "string", "isFinalised": true, "toActivityName": "string", "toStateName": "string", "transitionClassifier": "NotSpecified", "transitionTime": "2019-08-24T14:15:22Z", "triggerName": "string", "startTransitionTime": "2019-08-24T14:15:22Z", "transitionDuration": 0 } ] }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.create-instance)Create instance of the process. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------------------------ | ----------------------------------------------------------- | | schemeCode | string or null | | processId | string<uuid> | | identityId | string or null | | impersonatedIdentityId | string or null | | calendarName | string or null | | initialProcessParameters | Array of objects or null (ProcessParameter) | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/create-instance https\://workflowengine.io/workflow-api/rpc/create-instance ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "schemeCode": "string", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityId": "string", "impersonatedIdentityId": "string", "calendarName": "string", "initialProcessParameters": [ { "name": "string", "value": null, "purpose": "Temporary" } ] }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.is-process-exists)Check existence of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/is-process-exists https\://workflowengine.io/workflow-api/rpc/is-process-exists ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy `{ "isExists": true }` ## [](#tag/RpcInstance/operation/workflow-api.rpc.get-process-history-count)Get the count of history records for the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-process-history-count https\://workflowengine.io/workflow-api/rpc/get-process-history-count ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy `{ "processHistoryCount": 0 }` ## [](#tag/RpcLog)RpcLog ## [](#tag/RpcLog/operation/workflow-api.rpc.log-debug)Logs a debug message with parameters. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------- | -------------- | | message | string or null | | parameters | object or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/log-debug https\://workflowengine.io/workflow-api/rpc/log-debug ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "message": "string", "parameters": { "property1": null, "property2": null } }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcLog/operation/workflow-api.rpc.log-debug-if-logger-exists)Logs a debug message with parameters if the logger exists. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------- | -------------- | | message | string or null | | parameters | object or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/log-debug-if-logger-exists https\://workflowengine.io/workflow-api/rpc/log-debug-if-logger-exists ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "message": "string", "parameters": { "property1": null, "property2": null } }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcLog/operation/workflow-api.rpc.log-error-if-logger-exists)Logs an error message with parameters if the logger exists. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------- | -------------- | | message | string or null | | parameters | object or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/log-error-if-logger-exists https\://workflowengine.io/workflow-api/rpc/log-error-if-logger-exists ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "message": "string", "parameters": { "property1": null, "property2": null } }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcLog/operation/workflow-api.rpc.log-error)Logs an error message with parameters. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------- | -------------- | | message | string or null | | parameters | object or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/log-error https\://workflowengine.io/workflow-api/rpc/log-error ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "message": "string", "parameters": { "property1": null, "property2": null } }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcLog/operation/workflow-api.rpc.log-info-if-logger-exists)Logs an info message with parameters if the logger exists. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------- | -------------- | | message | string or null | | parameters | object or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/log-info-if-logger-exists https\://workflowengine.io/workflow-api/rpc/log-info-if-logger-exists ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "message": "string", "parameters": { "property1": null, "property2": null } }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcLog/operation/workflow-api.rpc.log-info)Logs an info message with parameters. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------- | -------------- | | message | string or null | | parameters | object or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/log-info https\://workflowengine.io/workflow-api/rpc/log-info ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "message": "string", "parameters": { "property1": null, "property2": null } }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcPreExecution)RpcPreExecution ## [](#tag/RpcPreExecution/operation/workflow-api.rpc.pre-execute-from-initial-activity)Pre-execute from the initial activity of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/pre-execute-from-initial-activity https\://workflowengine.io/workflow-api/rpc/pre-execute-from-initial-activity ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "activitiesSequence": [ { "name": "string", "state": "string", "isCurrent": true, "isFinal": true, "isInitial": true, "subprocessLevel": 0, "localizedState": "string", "transitions": [ { "commandName": "string", "nextActivityName": "string", "triggerType": "string", "classifier": "NotSpecified", "localizedCommandName": "string" } ] } ] }` ## [](#tag/RpcPreExecution/operation/workflow-api.rpc.pre-execute-from-current-activity)Pre-execute from the current activity of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/pre-execute-from-current-activity https\://workflowengine.io/workflow-api/rpc/pre-execute-from-current-activity ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "activitiesSequence": [ { "name": "string", "state": "string", "isCurrent": true, "isFinal": true, "isInitial": true, "subprocessLevel": 0, "localizedState": "string", "transitions": [ { "commandName": "string", "nextActivityName": "string", "triggerType": "string", "classifier": "NotSpecified", "localizedCommandName": "string" } ] } ] }` ## [](#tag/RpcPreExecution/operation/workflow-api.rpc.pre-execute)Pre-execute from the specified activity of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------- | -------------------------------------------- | | processId | string<uuid> | | fromActivityName | string or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/pre-execute https\://workflowengine.io/workflow-api/rpc/pre-execute ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "fromActivityName": "string" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "activitiesSequence": [ { "name": "string", "state": "string", "isCurrent": true, "isFinal": true, "isInitial": true, "subprocessLevel": 0, "localizedState": "string", "transitions": [ { "commandName": "string", "nextActivityName": "string", "triggerType": "string", "classifier": "NotSpecified", "localizedCommandName": "string" } ] } ] }` ## [](#tag/RpcRuntime)RpcRuntime ## [](#tag/RpcRuntime/operation/workflow-api.rpc.runtime-cold-start)Starts the workflow runtime in cold start mode. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired object ( RuntimeColdStartRequest ) ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/runtime-cold-start https\://workflowengine.io/workflow-api/rpc/runtime-cold-start ### Request samples * Payload Content type application/json Copy `{ }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcRuntime/operation/workflow-api.rpc.runtime-get-running-status)Gets the status of the workflow runtime. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired object ( RuntimeGetStatusRequest ) ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/runtime-get-running-status https\://workflowengine.io/workflow-api/rpc/runtime-get-running-status ### Request samples * Payload Content type application/json Copy `{ }` ### Response samples * 200 * 400 Content type application/json Copy `{ "runningStatus": "Stopped" }` ## [](#tag/RpcRuntime/operation/workflow-api.rpc.runtime-shut-down)Shuts down the workflow runtime. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | ---------------------------------------------- | | timeout | integer<int32> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/runtime-shut-down https\://workflowengine.io/workflow-api/rpc/runtime-shut-down ### Request samples * Payload Content type application/json Copy `{ "timeout": 0 }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcRuntime/operation/workflow-api.rpc.runtime-start)Starts the workflow runtime. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired object ( RuntimeStartRequest ) ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/runtime-start https\://workflowengine.io/workflow-api/rpc/runtime-start ### Request samples * Payload Content type application/json Copy `{ }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcScheme)RpcScheme ## [](#tag/RpcScheme/operation/workflow-api.rpc.update-scheme-if-obsolete)Update scheme of the process instance if it is obsolete. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | -------------------------------------------- | | processId | string<uuid> | | ignoreAutoSchemeUpdate | boolean | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/update-scheme-if-obsolete https\://workflowengine.io/workflow-api/rpc/update-scheme-if-obsolete ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "ignoreAutoSchemeUpdate": true }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcScheme/operation/workflow-api.rpc.get-process-scheme)Get the scheme of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-process-scheme https\://workflowengine.io/workflow-api/rpc/get-process-scheme ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "processScheme": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "actors": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } ], "parameters": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" } ], "commands": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" } ], "timers": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } ], "comments": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "value": "string", "boldText": true, "italicText": true, "underlineText": true, "lineThroughText": true, "fontSize": 0, "rotation": 0.1, "width": 0.1, "alignment": "string" } ], "activities": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 } ], "transitions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inlinedFinalActivityName": "string", "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "userComment": "string", "wasInlined": true, "from": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "to": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "classifier": "string", "trigger": { "type": "string", "command": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": null, "y": null, "bending": null, "scale": null, "color": null, "autoTextContrast": null, "group": null, "hidden": null, "overwriteActivityTo": null, "inlineElementSettings": null }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" }, "timer": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } }, "conditions": [ { "type": "string", "action": { "actionName": "string", "order": 0, "actionParameter": "string" }, "expression": "string", "resultOnPreExecution": true, "conditionInversion": true } ], "restrictions": [ { "type": "string", "actor": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } } ], "allowConcatenationType": "string", "restrictConcatenationType": "string", "conditionsConcatenationType": "string", "annotations": [ { "name": "string", "jsonValue": "string" } ], "isFork": true, "mergeViaSetState": true, "disableParentStateControl": true, "subprocessStartupType": "string", "subprocessInOutDefinition": "string", "subprocessName": "string", "subprocessId": "string", "subprocessStartupParameterCopyStrategy": "string", "subprocessFinalizeParameterMergeStrategy": "string", "subprocessSpecifiedParameters": "string", "isAlwaysTransition": true, "isOtherwiseTransition": true, "isConditionTransition": true } ], "localization": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "objectName": "string", "value": "string", "type": "string", "culture": "string", "isDefault": true } ], "codeActions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "actionCode": "string", "type": "string", "isGlobal": true, "isAsync": true, "usings": "string", "excludedUsings": "string", "parameterDefinitions": [ { "title": "string", "name": "string", "type": "string", "isRequired": true, "dropdownValues": [ { "name": "string", "value": "string" } ], "defaultValue": "string", "comment": "string", "customName": "string" } ] } ], "codeActionsCommonUsings": "string", "additionalParams": { "property1": null, "property2": null }, "canBeInlined": true, "logEnabled": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ], "rootSchemeCode": "string", "rootSchemeId": "456b9878-a4c2-4734-a97a-e78fe61a04f5", "isObsolete": true, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "allowedActivities": [ "string" ], "calendarName": "string" } }` ## [](#tag/RpcScheme/operation/workflow-api.rpc.get-scheme-codes)Get scheme codes with optional filtering by tags. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---- | ------------------------ | | tags | Array of strings or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-scheme-codes https\://workflowengine.io/workflow-api/rpc/get-scheme-codes ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "tags": [ "string" ] }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "schemeCodes": [ "string" ] }` ## [](#tag/RpcScheme/operation/workflow-api.rpc.set-scheme-is-obsolete)Set all process schemes with the specified scheme code as obsolete. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------- | -------------- | | schemeCode | string or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/set-scheme-is-obsolete https\://workflowengine.io/workflow-api/rpc/set-scheme-is-obsolete ### Request samples * Payload Content type application/json Copy `{ "schemeCode": "string" }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcState)RpcState ## [](#tag/RpcState/operation/workflow-api.rpc.get-current-state-name)Get the current state name of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-current-state-name https\://workflowengine.io/workflow-api/rpc/get-current-state-name ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy `{ "currentStateName": "string" }` ## [](#tag/RpcState/operation/workflow-api.rpc.set-activity-without-execution)Set activity for the process instance without execution. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------------------- | -------------------------------------------- | | processId | string<uuid> | | activityName | string or null | | doNotSetRunningStatus | boolean | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/set-activity-without-execution https\://workflowengine.io/workflow-api/rpc/set-activity-without-execution ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "activityName": "string", "doNotSetRunningStatus": true }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcState/operation/workflow-api.rpc.set-state-with-execution)Set state for the process instance with execution. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ----------------------------------------------------------- | | processId | string<uuid> | | identityId | string or null | | impersonatedIdentityId | string or null | | stateName | string or null | | processParameters | Array of objects or null (ProcessParameter) | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/set-state-with-execution https\://workflowengine.io/workflow-api/rpc/set-state-with-execution ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "identityId": "string", "impersonatedIdentityId": "string", "stateName": "string", "processParameters": [ { "name": "string", "value": null, "purpose": "Temporary" } ] }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcState/operation/workflow-api.rpc.get-current-state)Get the current state of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | | culture | string or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-current-state https\://workflowengine.io/workflow-api/rpc/get-current-state ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "culture": "string" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "currentState": { "name": "string", "localizedName": "string", "schemeCode": "string" } }` ## [](#tag/RpcState/operation/workflow-api.rpc.set-activity-with-execution)Set activity for the process instance with execution. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ----------------------------------------------------------- | | processId | string<uuid> | | activityName | string or null | | processParameters | Array of objects or null (ProcessParameter) | | identityId | string or null | | impersonatedIdentityId | string or null | | doNotSetRunningStatus | boolean | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/set-activity-with-execution https\://workflowengine.io/workflow-api/rpc/set-activity-with-execution ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "activityName": "string", "processParameters": [ { "name": "string", "value": null, "purpose": "Temporary" } ], "identityId": "string", "impersonatedIdentityId": "string", "doNotSetRunningStatus": true }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcState/operation/workflow-api.rpc.get-available-states-to-set)Get the list of all available to set states for the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | | culture | string or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-available-states-to-set https\://workflowengine.io/workflow-api/rpc/get-available-states-to-set ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "culture": "string" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "availableStates": [ { "name": "string", "localizedName": "string", "schemeCode": "string" } ] }` ## [](#tag/RpcState/operation/workflow-api.rpc.set-state-without-execution)Set state for the process instance without execution. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | | stateName | string or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/set-state-without-execution https\://workflowengine.io/workflow-api/rpc/set-state-without-execution ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "stateName": "string" }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/RpcState/operation/workflow-api.rpc.resume)Resumes the process instance execution. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------------------- | ----------------------------------------------------------- | | processId | string<uuid> | | activityName | string or null | | identityId | string or null | | impersonatedIdentityId | string or null | | processParameters | Array of objects or null (ProcessParameter) | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/resume https\://workflowengine.io/workflow-api/rpc/resume ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "activityName": "string", "identityId": "string", "impersonatedIdentityId": "string", "processParameters": [ { "name": "string", "value": null, "purpose": "Temporary" } ] }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "wasResumed": true, "processInstance": { "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "parentProcessId": "0b8e9096-348a-4528-9c4f-06a402b244ad", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "schemeId": "d50fd494-6cfc-4f45-83a9-47603a003465", "previousState": "string", "previousStateForDirect": "string", "previousStateForReverse": "string", "previousActivityName": "string", "previousActivityForDirectName": "string", "previousActivityForReverseName": "string", "currentActivityName": "string", "schemeCode": "string", "currentState": "string", "tenantId": "string", "identityId": "string", "impersonatedIdentityId": "string", "subprocessName": "string", "creationDate": "2019-08-24T14:15:22Z", "lastTransitionDate": "2019-08-24T14:15:22Z", "logEnabled": true, "calendarName": "string", "isSchemeObsolete": true, "isSubprocess": true, "processParameters": [ { "name": "string", "purpose": "Temporary", "type": "string", "value": null, "isImplicit": true } ] } }` ## [](#tag/RpcState/operation/workflow-api.rpc.get-available-states-to-set-by-scheme-code)Get the list of all available to set states for the scheme. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------- | -------------- | | schemeCode | string or null | | culture | string or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-available-states-to-set-by-scheme-code https\://workflowengine.io/workflow-api/rpc/get-available-states-to-set-by-scheme-code ### Request samples * Payload Content type application/json Copy `{ "schemeCode": "string", "culture": "string" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "availableStates": [ { "name": "string", "localizedName": "string", "schemeCode": "string" } ] }` ## [](#tag/RpcState/operation/workflow-api.rpc.get-initial-state)Get the initial state of the scheme. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ---------- | -------------- | | schemeCode | string or null | | culture | string or null | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-initial-state https\://workflowengine.io/workflow-api/rpc/get-initial-state ### Request samples * Payload Content type application/json Copy `{ "schemeCode": "string", "culture": "string" }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "initialState": { "name": "string", "localizedName": "string", "schemeCode": "string" } }` ## [](#tag/RpcState/operation/workflow-api.rpc.get-current-activity-name)Get the current activity name of the process instance. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | --------- | -------------------------------------------- | | processId | string<uuid> | ### Responses **200** OK **400** Bad Request post/workflow-api/rpc/get-current-activity-name https\://workflowengine.io/workflow-api/rpc/get-current-activity-name ### Request samples * Payload Content type application/json Copy `{ "processId": "9e0ad09b-5150-48c0-aded-707587048fd9" }` ### Response samples * 200 * 400 Content type application/json Copy `{ "currentActivityName": "string" }` ## [](#tag/Runtimes)Runtimes ## [](#tag/Runtimes/operation/workflow-api.data.runtimes.get)Retrieve a single runtime by ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ---------- | ------ | | idrequired | string | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/runtimes/{id} https\://workflowengine.io/workflow-api/data/runtimes/{id} ### Response samples * 200 * 400 * 404 Content type application/json Copy `{ "id": "string", "lock": "3cb27986-df09-4549-a834-b2869eecacb5", "status": "Alive", "restorerId": "string", "nextTimerTime": "2019-08-24T14:15:22Z", "nextServiceTimerTime": "2019-08-24T14:15:22Z", "lastAliveSignal": "2019-08-24T14:15:22Z" }` ## [](#tag/Runtimes/operation/workflow-api.data.runtimes.get-collection)Retrieve runtimes collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (RuntimeFieldFilter) | | sorts | Array of objects (RuntimeFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/runtimes https\://workflowengine.io/workflow-api/data/runtimes ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "string", "lock": "3cb27986-df09-4549-a834-b2869eecacb5", "status": "Alive", "restorerId": "string", "nextTimerTime": "2019-08-24T14:15:22Z", "nextServiceTimerTime": "2019-08-24T14:15:22Z", "lastAliveSignal": "2019-08-24T14:15:22Z" } ], "total": 0 }` ## [](#tag/Runtimes/operation/workflow-api.search.runtimes)Retrieve runtimes collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | ------------------------------------------------------------- | | search | string or null | | filters | Array of objects or null (RuntimeFieldFilter) | | sorts | Array of objects or null (RuntimeFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/runtimes https\://workflowengine.io/workflow-api/search/runtimes ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "RuntimeId", "value": null } ], "sorts": [ { "field": "RuntimeId", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "string", "lock": "3cb27986-df09-4549-a834-b2869eecacb5", "status": "Alive", "restorerId": "string", "nextTimerTime": "2019-08-24T14:15:22Z", "nextServiceTimerTime": "2019-08-24T14:15:22Z", "lastAliveSignal": "2019-08-24T14:15:22Z" } ], "total": 0 }` ## [](#tag/Schemes)Schemes ## [](#tag/Schemes/operation/workflow-api.data.schemes.get)Retrieve a single scheme by code. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------ | ------ | | coderequired | string | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/schemes/{code} https\://workflowengine.io/workflow-api/data/schemes/{code} ### Response samples * 200 * 400 * 404 Content type application/json Copy Expand all Collapse all `{ "code": "string", "scheme": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "actors": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } ], "parameters": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" } ], "commands": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" } ], "timers": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } ], "comments": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "value": "string", "boldText": true, "italicText": true, "underlineText": true, "lineThroughText": true, "fontSize": 0, "rotation": 0.1, "width": 0.1, "alignment": "string" } ], "activities": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 } ], "transitions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inlinedFinalActivityName": "string", "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "userComment": "string", "wasInlined": true, "from": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "to": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "classifier": "string", "trigger": { "type": "string", "command": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": null, "y": null, "bending": null, "scale": null, "color": null, "autoTextContrast": null, "group": null, "hidden": null, "overwriteActivityTo": null, "inlineElementSettings": null }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" }, "timer": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } }, "conditions": [ { "type": "string", "action": { "actionName": "string", "order": 0, "actionParameter": "string" }, "expression": "string", "resultOnPreExecution": true, "conditionInversion": true } ], "restrictions": [ { "type": "string", "actor": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } } ], "allowConcatenationType": "string", "restrictConcatenationType": "string", "conditionsConcatenationType": "string", "annotations": [ { "name": "string", "jsonValue": "string" } ], "isFork": true, "mergeViaSetState": true, "disableParentStateControl": true, "subprocessStartupType": "string", "subprocessInOutDefinition": "string", "subprocessName": "string", "subprocessId": "string", "subprocessStartupParameterCopyStrategy": "string", "subprocessFinalizeParameterMergeStrategy": "string", "subprocessSpecifiedParameters": "string", "isAlwaysTransition": true, "isOtherwiseTransition": true, "isConditionTransition": true } ], "localization": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "objectName": "string", "value": "string", "type": "string", "culture": "string", "isDefault": true } ], "codeActions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "actionCode": "string", "type": "string", "isGlobal": true, "isAsync": true, "usings": "string", "excludedUsings": "string", "parameterDefinitions": [ { "title": "string", "name": "string", "type": "string", "isRequired": true, "dropdownValues": [ { "name": "string", "value": "string" } ], "defaultValue": "string", "comment": "string", "customName": "string" } ] } ], "codeActionsCommonUsings": "string", "additionalParams": { "property1": null, "property2": null }, "canBeInlined": true, "logEnabled": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ], "rootSchemeCode": "string", "rootSchemeId": "456b9878-a4c2-4734-a97a-e78fe61a04f5", "isObsolete": true, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "allowedActivities": [ "string" ], "calendarName": "string" }, "canBeInlined": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ] }` ## [](#tag/Schemes/operation/workflow-api.data.schemes.create)Create a single scheme using code and creation request. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------ | ------ | | coderequired | string | ##### Request Body schema: application/jsonrequired | | | | -------------- | ------------------------------- | | scheme | object (Scheme) | | canBeInlined | boolean | | inlinedSchemes | Array of strings or null | | tags | Array of strings or null | ### Responses **200** OK **400** Bad Request post/workflow-api/data/schemes/{code} https\://workflowengine.io/workflow-api/data/schemes/{code} ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "scheme": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "actors": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } ], "parameters": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" } ], "commands": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" } ], "timers": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } ], "comments": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "value": "string", "boldText": true, "italicText": true, "underlineText": true, "lineThroughText": true, "fontSize": 0, "rotation": 0.1, "width": 0.1, "alignment": "string" } ], "activities": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 } ], "transitions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inlinedFinalActivityName": "string", "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "userComment": "string", "wasInlined": true, "from": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "to": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "classifier": "string", "trigger": { "type": "string", "command": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": null, "y": null, "bending": null, "scale": null, "color": null, "autoTextContrast": null, "group": null, "hidden": null, "overwriteActivityTo": null, "inlineElementSettings": null }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" }, "timer": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } }, "conditions": [ { "type": "string", "action": { "actionName": "string", "order": 0, "actionParameter": "string" }, "expression": "string", "resultOnPreExecution": true, "conditionInversion": true } ], "restrictions": [ { "type": "string", "actor": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } } ], "allowConcatenationType": "string", "restrictConcatenationType": "string", "conditionsConcatenationType": "string", "annotations": [ { "name": "string", "jsonValue": "string" } ], "isFork": true, "mergeViaSetState": true, "disableParentStateControl": true, "subprocessStartupType": "string", "subprocessInOutDefinition": "string", "subprocessName": "string", "subprocessId": "string", "subprocessStartupParameterCopyStrategy": "string", "subprocessFinalizeParameterMergeStrategy": "string", "subprocessSpecifiedParameters": "string", "isAlwaysTransition": true, "isOtherwiseTransition": true, "isConditionTransition": true } ], "localization": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "objectName": "string", "value": "string", "type": "string", "culture": "string", "isDefault": true } ], "codeActions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "actionCode": "string", "type": "string", "isGlobal": true, "isAsync": true, "usings": "string", "excludedUsings": "string", "parameterDefinitions": [ { "title": "string", "name": "string", "type": "string", "isRequired": true, "dropdownValues": [ { "name": "string", "value": "string" } ], "defaultValue": "string", "comment": "string", "customName": "string" } ] } ], "codeActionsCommonUsings": "string", "additionalParams": { "property1": null, "property2": null }, "canBeInlined": true, "logEnabled": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ], "rootSchemeCode": "string", "rootSchemeId": "456b9878-a4c2-4734-a97a-e78fe61a04f5", "isObsolete": true, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "allowedActivities": [ "string" ], "calendarName": "string" }, "canBeInlined": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ] }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/Schemes/operation/workflow-api.data.schemes.update)Update a single scheme by code, using an update request. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------ | ------ | | coderequired | string | ##### Request Body schema: application/jsonrequired | | | | -------------- | ------------------------------- | | code | string or null | | scheme | object (Scheme) | | canBeInlined | boolean or null | | inlinedSchemes | Array of strings or null | | tags | Array of strings or null | ### Responses **200** OK **400** Bad Request put/workflow-api/data/schemes/{code} https\://workflowengine.io/workflow-api/data/schemes/{code} ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "code": "string", "scheme": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "actors": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } ], "parameters": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" } ], "commands": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" } ], "timers": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } ], "comments": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "value": "string", "boldText": true, "italicText": true, "underlineText": true, "lineThroughText": true, "fontSize": 0, "rotation": 0.1, "width": 0.1, "alignment": "string" } ], "activities": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 } ], "transitions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inlinedFinalActivityName": "string", "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "userComment": "string", "wasInlined": true, "from": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "to": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "classifier": "string", "trigger": { "type": "string", "command": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": null, "y": null, "bending": null, "scale": null, "color": null, "autoTextContrast": null, "group": null, "hidden": null, "overwriteActivityTo": null, "inlineElementSettings": null }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" }, "timer": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } }, "conditions": [ { "type": "string", "action": { "actionName": "string", "order": 0, "actionParameter": "string" }, "expression": "string", "resultOnPreExecution": true, "conditionInversion": true } ], "restrictions": [ { "type": "string", "actor": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } } ], "allowConcatenationType": "string", "restrictConcatenationType": "string", "conditionsConcatenationType": "string", "annotations": [ { "name": "string", "jsonValue": "string" } ], "isFork": true, "mergeViaSetState": true, "disableParentStateControl": true, "subprocessStartupType": "string", "subprocessInOutDefinition": "string", "subprocessName": "string", "subprocessId": "string", "subprocessStartupParameterCopyStrategy": "string", "subprocessFinalizeParameterMergeStrategy": "string", "subprocessSpecifiedParameters": "string", "isAlwaysTransition": true, "isOtherwiseTransition": true, "isConditionTransition": true } ], "localization": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "objectName": "string", "value": "string", "type": "string", "culture": "string", "isDefault": true } ], "codeActions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "actionCode": "string", "type": "string", "isGlobal": true, "isAsync": true, "usings": "string", "excludedUsings": "string", "parameterDefinitions": [ { "title": "string", "name": "string", "type": "string", "isRequired": true, "dropdownValues": [ { "name": "string", "value": "string" } ], "defaultValue": "string", "comment": "string", "customName": "string" } ] } ], "codeActionsCommonUsings": "string", "additionalParams": { "property1": null, "property2": null }, "canBeInlined": true, "logEnabled": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ], "rootSchemeCode": "string", "rootSchemeId": "456b9878-a4c2-4734-a97a-e78fe61a04f5", "isObsolete": true, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "allowedActivities": [ "string" ], "calendarName": "string" }, "canBeInlined": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ] }` ### Response samples * 200 * 400 Content type application/json Copy `{ "updatedCount": 0 }` ## [](#tag/Schemes/operation/workflow-api.data.schemes.delete)Delete a single scheme by code. ##### Authorizations: *Bearer* ##### path Parameters | | | | ------------ | ------ | | coderequired | string | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/schemes/{code} https\://workflowengine.io/workflow-api/data/schemes/{code} ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Schemes/operation/workflow-api.data.schemes.get-collection)Retrieve schemes collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (SchemeFieldFilter) | | sorts | Array of objects (SchemeFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/schemes https\://workflowengine.io/workflow-api/data/schemes ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "code": "string", "scheme": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "actors": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } ], "parameters": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" } ], "commands": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": null, "y": null, "bending": null, "scale": null, "color": null, "autoTextContrast": null, "group": null, "hidden": null, "overwriteActivityTo": null, "inlineElementSettings": null }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" } ], "timers": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } ], "comments": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "value": "string", "boldText": true, "italicText": true, "underlineText": true, "lineThroughText": true, "fontSize": 0, "rotation": 0.1, "width": 0.1, "alignment": "string" } ], "activities": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 } ], "transitions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inlinedFinalActivityName": "string", "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "userComment": "string", "wasInlined": true, "from": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ null ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "to": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ null ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "classifier": "string", "trigger": { "type": "string", "command": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": null, "isRequired": null, "defaultValue": null, "parameter": null, "comment": null } ], "comment": "string" }, "timer": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } }, "conditions": [ { "type": "string", "action": { "actionName": "string", "order": 0, "actionParameter": "string" }, "expression": "string", "resultOnPreExecution": true, "conditionInversion": true } ], "restrictions": [ { "type": "string", "actor": { "name": "string", "designerSettings": { "x": null, "y": null, "bending": null, "scale": null, "color": null, "autoTextContrast": null, "group": null, "hidden": null, "overwriteActivityTo": null, "inlineElementSettings": null }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } } ], "allowConcatenationType": "string", "restrictConcatenationType": "string", "conditionsConcatenationType": "string", "annotations": [ { "name": "string", "jsonValue": "string" } ], "isFork": true, "mergeViaSetState": true, "disableParentStateControl": true, "subprocessStartupType": "string", "subprocessInOutDefinition": "string", "subprocessName": "string", "subprocessId": "string", "subprocessStartupParameterCopyStrategy": "string", "subprocessFinalizeParameterMergeStrategy": "string", "subprocessSpecifiedParameters": "string", "isAlwaysTransition": true, "isOtherwiseTransition": true, "isConditionTransition": true } ], "localization": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "objectName": "string", "value": "string", "type": "string", "culture": "string", "isDefault": true } ], "codeActions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "actionCode": "string", "type": "string", "isGlobal": true, "isAsync": true, "usings": "string", "excludedUsings": "string", "parameterDefinitions": [ { "title": "string", "name": "string", "type": "string", "isRequired": true, "dropdownValues": [ { "name": null, "value": null } ], "defaultValue": "string", "comment": "string", "customName": "string" } ] } ], "codeActionsCommonUsings": "string", "additionalParams": { "property1": null, "property2": null }, "canBeInlined": true, "logEnabled": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ], "rootSchemeCode": "string", "rootSchemeId": "456b9878-a4c2-4734-a97a-e78fe61a04f5", "isObsolete": true, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "allowedActivities": [ "string" ], "calendarName": "string" }, "canBeInlined": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ] } ], "total": 0 }` ## [](#tag/Schemes/operation/workflow-api.data.schemes.delete-collection)Delete multiple schemes based on optional filters. ##### Authorizations: *Bearer* ##### query Parameters | | | | ------- | ---------------------------------------------------- | | filters | Array of objects (SchemeFieldFilter) | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/schemes https\://workflowengine.io/workflow-api/data/schemes ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Schemes/operation/workflow-api.data.schemes.create-collection)Create multiple schemes from an array of creation requests. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired Array | | | | -------------- | ------------------------------- | | code | string or null | | scheme | object (Scheme) | | canBeInlined | boolean | | inlinedSchemes | Array of strings or null | | tags | Array of strings or null | ### Responses **200** OK **400** Bad Request post/workflow-api/data/schemes https\://workflowengine.io/workflow-api/data/schemes ### Request samples * Payload Content type application/json Copy Expand all Collapse all `[ { "code": "string", "scheme": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "actors": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } ], "parameters": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" } ], "commands": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" } ], "timers": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } ], "comments": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "value": "string", "boldText": true, "italicText": true, "underlineText": true, "lineThroughText": true, "fontSize": 0, "rotation": 0.1, "width": 0.1, "alignment": "string" } ], "activities": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 } ], "transitions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inlinedFinalActivityName": "string", "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "userComment": "string", "wasInlined": true, "from": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "to": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "classifier": "string", "trigger": { "type": "string", "command": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": null, "designerSettings": null, "type": null, "purpose": null, "initialValue": null }, "comment": "string" } ], "comment": "string" }, "timer": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } }, "conditions": [ { "type": "string", "action": { "actionName": "string", "order": 0, "actionParameter": "string" }, "expression": "string", "resultOnPreExecution": true, "conditionInversion": true } ], "restrictions": [ { "type": "string", "actor": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } } ], "allowConcatenationType": "string", "restrictConcatenationType": "string", "conditionsConcatenationType": "string", "annotations": [ { "name": "string", "jsonValue": "string" } ], "isFork": true, "mergeViaSetState": true, "disableParentStateControl": true, "subprocessStartupType": "string", "subprocessInOutDefinition": "string", "subprocessName": "string", "subprocessId": "string", "subprocessStartupParameterCopyStrategy": "string", "subprocessFinalizeParameterMergeStrategy": "string", "subprocessSpecifiedParameters": "string", "isAlwaysTransition": true, "isOtherwiseTransition": true, "isConditionTransition": true } ], "localization": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "objectName": "string", "value": "string", "type": "string", "culture": "string", "isDefault": true } ], "codeActions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "actionCode": "string", "type": "string", "isGlobal": true, "isAsync": true, "usings": "string", "excludedUsings": "string", "parameterDefinitions": [ { "title": "string", "name": "string", "type": "string", "isRequired": true, "dropdownValues": [ { "name": "string", "value": "string" } ], "defaultValue": "string", "comment": "string", "customName": "string" } ] } ], "codeActionsCommonUsings": "string", "additionalParams": { "property1": null, "property2": null }, "canBeInlined": true, "logEnabled": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ], "rootSchemeCode": "string", "rootSchemeId": "456b9878-a4c2-4734-a97a-e78fe61a04f5", "isObsolete": true, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "allowedActivities": [ "string" ], "calendarName": "string" }, "canBeInlined": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ] } ]` ### Response samples * 200 * 400 Content type application/json Copy `{ "createdCount": 0 }` ## [](#tag/Schemes/operation/workflow-api.search.schemes)Retrieve schemes collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | ------------------------------------------------------------ | | search | string or null | | filters | Array of objects or null (SchemeFieldFilter) | | sorts | Array of objects or null (SchemeFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/schemes https\://workflowengine.io/workflow-api/search/schemes ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "Code", "value": null } ], "sorts": [ { "field": "Code", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "code": "string", "scheme": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "actors": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } ], "parameters": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "purpose": "string", "initialValue": "string" } ], "commands": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": "string", "isRequired": true, "defaultValue": "string", "parameter": { "name": "string", "designerSettings": { "x": null, "y": null, "bending": null, "scale": null, "color": null, "autoTextContrast": null, "group": null, "hidden": null, "overwriteActivityTo": null, "inlineElementSettings": null }, "type": "string", "purpose": "string", "initialValue": "string" }, "comment": "string" } ], "comment": "string" } ], "timers": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } ], "comments": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "value": "string", "boldText": true, "italicText": true, "underlineText": true, "lineThroughText": true, "fontSize": 0, "rotation": 0.1, "width": 0.1, "alignment": "string" } ], "activities": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ "string" ], "order": 0 } ], "isState": true, "nestingLevel": 0 } ], "transitions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inlinedFinalActivityName": "string", "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "userComment": "string", "wasInlined": true, "from": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ null ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "to": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "lastTimeInlineName": "string", "firstTimeInlineName": "string", "wasInlined": true, "activityType": "string", "schemeCode": "string", "state": "string", "isInitial": true, "isFinal": true, "isForSetState": true, "isAutoSchemeUpdate": true, "inlinedSchemeCode": "string", "disablePersistState": true, "disablePersistTransitionHistory": true, "disablePersistParameters": true, "userComment": "string", "haveImplementation": true, "havePreExecutionImplementation": true, "implementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "preExecutionImplementation": [ { "actionName": "string", "order": 0, "actionParameter": "string" } ], "annotations": [ { "name": "string", "jsonValue": "string" } ], "executionTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "idleTimeout": { "type": "string", "timer": "string", "nameForSet": "string", "retryCount": 0 }, "exceptionsHandlers": [ { "type": "string", "nameForSet": "string", "retryCount": 0, "exceptions": [ null ], "order": 0 } ], "isState": true, "nestingLevel": 0 }, "classifier": "string", "trigger": { "type": "string", "command": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "inputParameters": [ { "name": null, "isRequired": null, "defaultValue": null, "parameter": null, "comment": null } ], "comment": "string" }, "timer": { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "type": "string", "value": "string", "notOverrideIfExists": true } }, "conditions": [ { "type": "string", "action": { "actionName": "string", "order": 0, "actionParameter": "string" }, "expression": "string", "resultOnPreExecution": true, "conditionInversion": true } ], "restrictions": [ { "type": "string", "actor": { "name": "string", "designerSettings": { "x": null, "y": null, "bending": null, "scale": null, "color": null, "autoTextContrast": null, "group": null, "hidden": null, "overwriteActivityTo": null, "inlineElementSettings": null }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "rule": "string", "value": "string", "isPredefined": true } } ], "allowConcatenationType": "string", "restrictConcatenationType": "string", "conditionsConcatenationType": "string", "annotations": [ { "name": "string", "jsonValue": "string" } ], "isFork": true, "mergeViaSetState": true, "disableParentStateControl": true, "subprocessStartupType": "string", "subprocessInOutDefinition": "string", "subprocessName": "string", "subprocessId": "string", "subprocessStartupParameterCopyStrategy": "string", "subprocessFinalizeParameterMergeStrategy": "string", "subprocessSpecifiedParameters": "string", "isAlwaysTransition": true, "isOtherwiseTransition": true, "isConditionTransition": true } ], "localization": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "objectName": "string", "value": "string", "type": "string", "culture": "string", "isDefault": true } ], "codeActions": [ { "name": "string", "designerSettings": { "x": "string", "y": "string", "bending": "string", "scale": "string", "color": "string", "autoTextContrast": true, "group": "string", "hidden": true, "overwriteActivityTo": "string", "inlineElementSettings": { } }, "originalName": "string", "originalSchemeCode": "string", "wasInlined": true, "actionCode": "string", "type": "string", "isGlobal": true, "isAsync": true, "usings": "string", "excludedUsings": "string", "parameterDefinitions": [ { "title": "string", "name": "string", "type": "string", "isRequired": true, "dropdownValues": [ { "name": null, "value": null } ], "defaultValue": "string", "comment": "string", "customName": "string" } ] } ], "codeActionsCommonUsings": "string", "additionalParams": { "property1": null, "property2": null }, "canBeInlined": true, "logEnabled": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ], "rootSchemeCode": "string", "rootSchemeId": "456b9878-a4c2-4734-a97a-e78fe61a04f5", "isObsolete": true, "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "allowedActivities": [ "string" ], "calendarName": "string" }, "canBeInlined": true, "inlinedSchemes": [ "string" ], "tags": [ "string" ] } ], "total": 0 }` ## [](#tag/Statuses)Statuses ## [](#tag/Statuses/operation/workflow-api.data.statuses.get-collection)Retrieve process statuses collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (StatusFieldFilter) | | sorts | Array of objects (StatusFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/statuses https\://workflowengine.io/workflow-api/data/statuses ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "statusCode": 0, "lock": "3cb27986-df09-4549-a834-b2869eecacb5", "runtimeId": "string", "setTime": "2019-08-24T14:15:22Z" } ], "total": 0 }` ## [](#tag/Statuses/operation/workflow-api.data.statuses.get)Retrieve a single process status by process ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/statuses/{processId} https\://workflowengine.io/workflow-api/data/statuses/{processId} ### Response samples * 200 * 400 * 404 Content type application/json Copy `{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "statusCode": 0, "lock": "3cb27986-df09-4549-a834-b2869eecacb5", "runtimeId": "string", "setTime": "2019-08-24T14:15:22Z" }` ## [](#tag/Statuses/operation/workflow-api.search.statuses)Retrieve process statuses collection and total count, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | ------------------------------------------------------------ | | search | string or null | | filters | Array of objects or null (StatusFieldFilter) | | sorts | Array of objects or null (StatusFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/statuses https\://workflowengine.io/workflow-api/search/statuses ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "Id", "value": null } ], "sorts": [ { "field": "Id", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "statusCode": 0, "lock": "3cb27986-df09-4549-a834-b2869eecacb5", "runtimeId": "string", "setTime": "2019-08-24T14:15:22Z" } ], "total": 0 }` ## [](#tag/Timers)Timers ## [](#tag/Timers/operation/workflow-api.search.processes.timers)Retrieve timers collection and total count across all process instances, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | ----------------------------------------------------------- | | search | string or null | | filters | Array of objects or null (TimerFieldFilter) | | sorts | Array of objects or null (TimerFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/processes/timers https\://workflowengine.io/workflow-api/search/processes/timers ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "Id", "value": null } ], "sorts": [ { "field": "Id", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "name": "string", "nextExecutionDateTime": "2019-08-24T14:15:22Z", "ignore": true } ], "total": 0 }` ## [](#tag/Timers/operation/workflow-api.data.processes.timers.update)Update a single timer for a specific process instance ID and timer name, using an update request. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | namerequired | string | ##### Request Body schema: application/jsonrequired | | | | --------------------- | --------------------------------------------------------- | | name | string or null | | nextExecutionDateTime | string or null<date-time> | | ignore | boolean or null | ### Responses **200** OK **400** Bad Request put/workflow-api/data/processes/{processId}/timers/{name} https\://workflowengine.io/workflow-api/data/processes/{processId}/timers/{name} ### Request samples * Payload Content type application/json Copy `{ "name": "string", "nextExecutionDateTime": "2019-08-24T14:15:22Z", "ignore": true }` ### Response samples * 200 * 400 Content type application/json Copy `{ "updatedCount": 0 }` ## [](#tag/Timers/operation/workflow-api.data.processes.timers.get)Retrieve a single timer for a specific process instance ID and timer name. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | namerequired | string | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/processes/{processId}/timers/{name} https\://workflowengine.io/workflow-api/data/processes/{processId}/timers/{name} ### Response samples * 200 * 400 * 404 Content type application/json Copy `{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "name": "string", "nextExecutionDateTime": "2019-08-24T14:15:22Z", "ignore": true }` ## [](#tag/Timers/operation/workflow-api.data.processes.timers.delete)Delete a single timer for a specific process instance ID and timer name. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | namerequired | string | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/timers/{name} https\://workflowengine.io/workflow-api/data/processes/{processId}/timers/{name} ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Timers/operation/workflow-api.data.processes.timers.create)Create a single timer for a specific process instance ID, using timer name and creation request. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | namerequired | string | ##### Request Body schema: application/jsonrequired | | | | --------------------- | ------------------------------------------------- | | rootProcessId | string<uuid> | | nextExecutionDateTime | string<date-time> | | ignore | boolean | ### Responses **200** OK **400** Bad Request post/workflow-api/data/processes/{processId}/timers/{name} https\://workflowengine.io/workflow-api/data/processes/{processId}/timers/{name} ### Request samples * Payload Content type application/json Copy `{ "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "nextExecutionDateTime": "2019-08-24T14:15:22Z", "ignore": true }` ### Response samples * 200 * 400 Content type application/json Copy `{ }` ## [](#tag/Timers/operation/workflow-api.data.processes.timers.delete-collection)Delete multiple timers for a specific process instance ID, with optional filters. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | --------------------------------------------------- | | filters | Array of objects (TimerFieldFilter) | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/timers https\://workflowengine.io/workflow-api/data/processes/{processId}/timers ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Timers/operation/workflow-api.data.processes.timers.get-collection)Retrieve timers collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (TimerFieldFilter) | | sorts | Array of objects (TimerFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/processes/{processId}/timers https\://workflowengine.io/workflow-api/data/processes/{processId}/timers ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "name": "string", "nextExecutionDateTime": "2019-08-24T14:15:22Z", "ignore": true } ], "total": 0 }` ## [](#tag/Timers/operation/workflow-api.data.processes.timers.create-collection)Create multiple timers for a specific process instance ID from an array of creation requests. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### Request Body schema: application/jsonrequired Array | | | | --------------------- | ------------------------------------------------- | | name | string or null | | rootProcessId | string<uuid> | | nextExecutionDateTime | string<date-time> | | ignore | boolean | ### Responses **200** OK **400** Bad Request post/workflow-api/data/processes/{processId}/timers https\://workflowengine.io/workflow-api/data/processes/{processId}/timers ### Request samples * Payload Content type application/json Copy Expand all Collapse all `[ { "name": "string", "rootProcessId": "40cf4e06-2f74-499b-989d-4bc9ab21fd8b", "nextExecutionDateTime": "2019-08-24T14:15:22Z", "ignore": true } ]` ### Response samples * 200 * 400 Content type application/json Copy `{ "createdCount": 0 }` ## [](#tag/Transitions)Transitions ## [](#tag/Transitions/operation/workflow-api.data.processes.transitions.delete-collection)Delete multiple transitions for a specific process instance ID, with optional filters. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | -------------------------------------------------------- | | filters | Array of objects (TransitionFieldFilter) | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/transitions https\://workflowengine.io/workflow-api/data/processes/{processId}/transitions ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Transitions/operation/workflow-api.data.processes.transitions.get-collection)Retrieve transitions collection and total count for a specific process instance ID, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | ##### query Parameters | | | | ------- | ----------------------------------------------------------------------- | | search | string | | filters | Array of objects (TransitionFieldFilter) | | sorts | Array of objects (TransitionFieldSort) | | skip | integer<int64>Default:0 | | take | integer<int64>Default:0 | ### Responses **200** OK **400** Bad Request get/workflow-api/data/processes/{processId}/transitions https\://workflowengine.io/workflow-api/data/processes/{processId}/transitions ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "executorIdentityId": "string", "actorIdentityId": "string", "executorName": "string", "actorName": "string", "fromActivityName": "string", "toActivityName": "string", "toStateName": "string", "transitionTime": "2019-08-24T14:15:22Z", "transitionClassifier": "string", "isFinalised": true, "fromStateName": "string", "triggerName": "string", "startTransitionTime": "2019-08-24T14:15:22Z", "transitionDuration": 0 } ], "total": 0 }` ## [](#tag/Transitions/operation/workflow-api.data.processes.transitions.get)Retrieve a single transition for a specific process instance ID and transition ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | idrequired | string<uuid> | ### Responses **200** OK **400** Bad Request **404** Not Found get/workflow-api/data/processes/{processId}/transitions/{id} https\://workflowengine.io/workflow-api/data/processes/{processId}/transitions/{id} ### Response samples * 200 * 400 * 404 Content type application/json Copy `{ "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "executorIdentityId": "string", "actorIdentityId": "string", "executorName": "string", "actorName": "string", "fromActivityName": "string", "toActivityName": "string", "toStateName": "string", "transitionTime": "2019-08-24T14:15:22Z", "transitionClassifier": "string", "isFinalised": true, "fromStateName": "string", "triggerName": "string", "startTransitionTime": "2019-08-24T14:15:22Z", "transitionDuration": 0 }` ## [](#tag/Transitions/operation/workflow-api.data.processes.transitions.delete)Delete a single transition for a specific process instance ID and transition ID. ##### Authorizations: *Bearer* ##### path Parameters | | | | ----------------- | -------------------------------------------- | | processIdrequired | string<uuid> | | idrequired | string<uuid> | ### Responses **200** OK **400** Bad Request delete/workflow-api/data/processes/{processId}/transitions/{id} https\://workflowengine.io/workflow-api/data/processes/{processId}/transitions/{id} ### Response samples * 200 * 400 Content type application/json Copy `{ "deletedCount": 0 }` ## [](#tag/Transitions/operation/workflow-api.search.processes.transitions)Retrieve transitions collection and total count across all process instances, with optional search, filters, sorting, and paging. ##### Authorizations: *Bearer* ##### Request Body schema: application/jsonrequired | | | | ------- | ---------------------------------------------------------------- | | search | string or null | | filters | Array of objects or null (TransitionFieldFilter) | | sorts | Array of objects or null (TransitionFieldSort) | | skip | integer<int64> | | take | integer<int64> | ### Responses **200** OK **400** Bad Request post/workflow-api/search/processes/transitions https\://workflowengine.io/workflow-api/search/processes/transitions ### Request samples * Payload Content type application/json Copy Expand all Collapse all `{ "search": "string", "filters": [ { "type": "And", "filters": [ { } ], "field": "Id", "value": null } ], "sorts": [ { "field": "Id", "direction": "Asc" } ], "skip": 0, "take": 0 }` ### Response samples * 200 * 400 Content type application/json Copy Expand all Collapse all `{ "collection": [ { "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", "processId": "9e0ad09b-5150-48c0-aded-707587048fd9", "executorIdentityId": "string", "actorIdentityId": "string", "executorName": "string", "actorName": "string", "fromActivityName": "string", "toActivityName": "string", "toStateName": "string", "transitionTime": "2019-08-24T14:15:22Z", "transitionClassifier": "string", "isFinalised": true, "fromStateName": "string", "triggerName": "string", "startTransitionTime": "2019-08-24T14:15:22Z", "transitionDuration": 0 } ], "total": 0 }` Contacts * Agreements * [License Agreement](https://optimajet.com/products/workflowengine/eula/) * [Customer Support Agreement](https://optimajet.com/products/workflowengine/csa/) Products * [Workflow Server](https://workflowserver.io) * [FormEngine](https://formengine.io) More * [GitHub](https://github.com/optimajet/WorkflowEngine.NET) * [WorkflowEngine Demo](https://demo.workflowengine.io/) [![OptimaJet Logo](/documentation/img/optimajet-logo.png)![OptimaJet Logo](/documentation/img/optimajet-logo.png)](https://optimajet.com) Copyright © 2026 Optimajet Limited. All rights reserved. --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- # Loading API Reference Loading projects and type index... --- [Skip to main content](#__docusaurus_skipToContent_fallback) [Introducing Formengine - The New Formbuilder, try for FREE formengine.io.](https://formengine.io/?utm_source=workflowengine\&utm_medium=topbanner) [![WorkflowEngine Logo](/documentation/img/logo.svg)![WorkflowEngine Logo](/documentation/img/logo.svg)](https://workflowengine.io/) [Discuss on GitHub](https://github.com/optimajet/WorkflowEngine.NET/discussions)[Features](https://workflowengine.io/features/)[Benefits](https://workflowengine.io/benefits/)[Demo](https://demo.workflowengine.io/designer)[Server](https://workflowengine.io/server/)[Documentation](https://workflowengine.io/documentation/)[Downloads](https://workflowengine.io/downloads/net-core/)[Pricing](https://workflowengine.io/pricing/)[Blog](https://workflowengine.io/blog/)[Contact](https://workflowengine.io/contacts/) Search # Search the documentation Contacts * Agreements * [License Agreement](https://optimajet.com/products/workflowengine/eula/) * [Customer Support Agreement](https://optimajet.com/products/workflowengine/csa/) Products * [Workflow Server](https://workflowserver.io) * [FormEngine](https://formengine.io) More * [GitHub](https://github.com/optimajet/WorkflowEngine.NET) * [WorkflowEngine Demo](https://demo.workflowengine.io/) [![OptimaJet Logo](/documentation/img/optimajet-logo.png)![OptimaJet Logo](/documentation/img/optimajet-logo.png)](https://optimajet.com) Copyright © 2026 Optimajet Limited. All rights reserved. --- # BPMN support in Workflow Engine It is important to note that Workflow Engine is not a [BPMN](https://www.bpmn.org) engine, which makes full support for the BPMN standard impossible. However, the principles of process execution in Workflow Engine do not contradict BPMN principles. Therefore, BPMN support in Workflow Engine is based on two main aspects: * **BPMN Elements in Workflow Engine Scheme:** We have added basic BPMN elements to Workflow Engine so that when a BPMN diagram is imported, familiar elements remain, making it clear where attention should be focused. Some of these elements are useful on their own, such as the Parallel Gateway, which can be beneficial for creating and merging subprocesses in Workflow Engine in a simpler and more understandable way. * **BPMN Import:** You can import a BPMN diagram, and it will be converted into a Workflow Engine scheme. A detailed log and comments will indicate what is unsupported or only partially supported in the current implementation. The structure of this section will be as follows. First, we will describe the differences between BPMN diagrams and Workflow Engine process schemes. Then, we will review which BPMN diagram elements are supported in Workflow Engine and how they are implemented. Finally, we will explain how to integrate BPMN import into an application running Workflow Engine and how to customize this import process. ## [📄️ BPMN diagrams vs Workflow Engine schemes](https://workflowengine.io/documentation/bpmn-differences) [Key differences between BPMN Diagrams and Workflow Engine process schemes](https://workflowengine.io/documentation/bpmn-differences) ## [📄️ BPMN elements support](https://workflowengine.io/documentation/bpmn-elements) [BPMN elements support in Workflow Engine](https://workflowengine.io/documentation/bpmn-elements) ## [📄️ Using BPMN plugin](https://workflowengine.io/documentation/bpmn-plugin) [Configuring and using BPMN plugin](https://workflowengine.io/documentation/bpmn-plugin) --- # Key differences between BPMN diagrams and Workflow Engine process schemes ## Element specialization[​](#element-specialization "Direct link to Element specialization") * In BPMN, there are a lot of elements that often have narrow specializations and are categorized into Tasks, Gateways, and Events, although other distinct elements exist as well. The Sequence flow is particularly significant and stands out. * In Workflow Engine, there are only two types of elements in the scheme — Activities and Transitions. By combining the configurations of these two elements, you can create Workflow Engine schemes that function similarly to the corresponding BPMN elements. ## Activity is not a Task[​](#activity-is-not-a-task "Direct link to Activity is not a Task") At first glance, an Activity appears as a rectangle, similar to a Task, and it may seem like these elements are alike. However, they're actually quite different. An Activity can represent a Service Task, a Gateway, or even an Event. The main function of an Activity is to execute your code, but the overall behavior of the scheme (i.e., the direction and manner in which the process flows next) depends on the settings of the outgoing Transitions from the Activity. For example, if an Activity has Transitions triggered by Commands, it acts similarly to an Event-based Gateway; however, unlike a Gateway, the Activity can execute some code. If the Transitions from an Activity are configured to start parallel processes, then that Activity will behave like a Parallel Gateway. And so on. ## Transition is not a Sequence Flow[​](#transition-is-not-a-sequence-flow "Direct link to Transition is not a Sequence Flow") At first glance, Transition and Sequence Flow may seem like similar elements, as both represent arrows between elements on a diagram. However, they're actually quite different. In Workflow Engine, a Transition defines how and where the process execution will transition. For example, a process can move to the next state based on a Timer or a Command. What is set by events in BPMN is managed through transitions in Workflow Engine. Additionally, Transitions can be conditional, and access to them can be restricted to specific users or roles. Transitions also determine parallelism, which in BPMN is either set through Gateways or implicitly defined. In BPMN, however, a Sequence Flow only defines the direction of the transition. This element has almost no settings, except for flows coming out of exclusive gateways. ## Parallelism difference[​](#parallelism-difference "Direct link to Parallelism difference") Parallelism in BPMN and Workflow Engine operates on different, yet non-contradictory principles. * In BPMN, parallel execution is defined either explicitly, using a Parallel Gateway, or implicitly, for example, when multiple Sequence Flows emerge from a single Service Task or Event. In this case, it is considered that the process remains singular, but several tokens are introduced. This can lead to a situation where, if you don't introduce a merging element or remove these tokens, multiple tokens will continue during sequential process execution. * In Workflow Engine, you can assume that there is always only one token within a single process, and parallelism is achieved by creating dependent processes ([called subprocesses in Workflow Engine](https://workflowengine.io/documentation/execution/subprocesses)). These subprocesses depend on the process that spawned them, and you can retrieve a list of Activities where the parent process and all its child subprocesses are located, as well as manage the parent process and its subprocesses collectively. The creation and merging of processes are handled by transition settings. However, if this approach is unfamiliar, you can use the Parallel Gateway, which replicates the behavior of its BPMN counterpart. ## Subprocesses in BPMN and Workflow Engine are completely different[​](#subprocesses-in-bpmn-and-workflow-engine-are-completely-different "Direct link to Subprocesses in BPMN and Workflow Engine are completely different") * In BPMN, a Subprocess is merely a nested part of the main diagram that can be collapsed. It can't be reused and is primarily for aesthetic purposes. Workflow Engine has a similar feature called [Inline Activity](https://workflowengine.io/documentation/execution/scheme-inlining). This is a special activity that embeds another scheme into the current one. The embedded scheme can be used in multiple other schemes, allowing for reusability. * In Workflow Engine, a subprocess is a dependent process, as mentioned earlier. ## Workflow Engine provides real access control to Commands[​](#workflow-engine-provides-real-access-control-to-commands "Direct link to Workflow Engine provides real access control to Commands") In Workflow Engine, Actors are part of the process, and you can set restrictions on command execution (similar to a Message Catch Event) by specific users directly within the scheme. BPMN doesn't offer such functionality explicitly. ## Execution ready approach[​](#execution-ready-approach "Direct link to Execution ready approach") A valid Workflow Engine scheme is always ready for execution and can be run on the Workflow Engine. In contrast, a valid BPMN diagram might just be an illustration, and no engine will necessarily be able to execute it. ### Ability to set the Process to any state[​](#ability-to-set-the-process-to-any-state "Direct link to Ability to set the Process to any state") In Workflow Engine, a running process can always be forcibly set to any of its states. BPMN doesn't offer such a capability. Armed with this knowledge, let's move on to exploring which BPMN elements are supported in Workflow Engine and how they’re implemented. --- # BPMN elements support in Workflow Engine We are continuously working on the implementation of BPMN for Workflow Engine, so support will be expanded over time. ## Tasks[​](#tasks "Direct link to Tasks") A Task is always converted into an Activity, with the Task ID recorded in the Activity Name, and the Task Name, if present, recorded in the Activity State. If multiple transitions come out of a Task, they will be converted to parallel transitions. * **Generic Task**. A task without a specific designation will be transformed into a standard Activity. * **Service Task**. This is supported and will be converted into a specialized Activity that visually resembles a typical Service Task. In the original task, the implementation will be based on the values of the attributes `"camunda:topic"`, `"camunda:delegateExpression"`, or the value of the implementation property. The resulting string is split by commas into substrings, which are then used as Action names in Workflow Engine. ![bpmn\_001](/documentation/assets/images/bpmn_001-cc79635fa0dfe35d4c6de46ac3c1f9a8.png) * **Receive Task**. This is supported and will be converted into a standard Activity, with a Transition emerging from it that is triggered by a Command. ![bpmn\_002](/documentation/assets/images/bpmn_002-b85c946c3067807a2223b76ddc87c46e.png) * **User Task**. Not supported yet; support will be added after the release of the plugin integrating Workflow Engine with forms. * **Business Rule Task**. Not supported; will be converted into a standard Activity. * **Manual Task**. Not supported; will be converted into a standard Activity. * **Script Task**. Not supported; will be converted into a standard Activity. * **Send Task**. Not supported; will be converted into a standard Activity. ## Gateways[​](#gateways "Direct link to Gateways") * **Exclusive Gateway**. This is supported and will be converted into a specialized Activity that visually resembles the typical Exclusive Gateway. Both Action-based conditions and expressions are supported. ![bpmn\_003](/documentation/assets/images/bpmn_003-41f76993e52d19e1cec8b7a86286dc13.png) * **Parallel Gateway**. This is supported and will be converted into a specialized Activity that visually resembles a typical Parallel Gateway. ![bpmn\_004](/documentation/assets/images/bpmn_004-c67fd03c701871a7a65e58a2786343ec.png) * **Event-Based Gateway**. This is supported and will be converted into a standard Activity with Transitions triggered by corresponding triggers, such as Command or Timer. ![bpmn\_005](/documentation/assets/images/bpmn_005-e4659bd23ca1e14f5124379e24d052e4.png) * **Inclusive Gateway**. Not supported due to how Workflow Engine handles subprocess creation. However, support can be added relatively quickly; please contact us if this is critical for you. In the current implementation, it will be converted into a standard Activity. * **Complex Gateway**. Not supported; will be converted into a standard Activity. ## Intermediate Catch Events[​](#intermediate-catch-events "Direct link to Intermediate Catch Events") * **Message**. This is supported and will be converted into a specialized Activity that visually resembles a typical Intermediate Message Catch Event. ![bpmn\_006](/documentation/assets/images/bpmn_006-1481a91777871dcb6ebac65092293f3a.png) * **Timer**. This is supported and will be converted into a specialized Activity that visually resembles a typical Intermediate Timer Catch Event. ![bpmn\_007](/documentation/assets/images/bpmn_007-1635fcdf230b597dfe949ae7ffa1ef1e.png) * **Signal**. Not supported; will be converted into a standard Activity. Support will be added once an Event-type trigger is introduced in Workflow Engine. * **Conditional**. Not supported; will be converted into a standard Activity. * **Link**. Not supported; will be converted into a standard Activity. ## Intermediate Throw Events[​](#intermediate-throw-events "Direct link to Intermediate Throw Events") This class of elements is not supported; any Intermediate Throw Event will be converted into a standard Activity. ## Boundary Events[​](#boundary-events "Direct link to Boundary Events") All Boundary Events are not converted into Activities in the scheme but into corresponding triggers on Transitions. * **Message**. Supported; a transition with a Command trigger will be added. ![bpmn\_008](/documentation/assets/images/bpmn_008-814ba873ba1b14c54c59649ca22c0664.png) * **Timer**. Supported; a transition with a Timer trigger will be added. ![bpmn\_009](/documentation/assets/images/bpmn_009-3a77cc3844e6839cf02156b59e59126b.png) * **Signal**. Not supported; an Auto Transition without a trigger will be created. Support will be added once an Event-type trigger is introduced in Workflow Engine. * **Escalation**. Not supported; an Auto Transition without a trigger will be created. * **Conditional**. Not supported; an Auto Transition without a trigger will be created. * **Error**. Not supported; an Auto Transition without a trigger will be created. * **Compensation**. Not supported; an Auto Transition without a trigger will be created. * **Non-interrupting Events**. Not supported; an Auto Transition without a trigger will be created. ## Start Events[​](#start-events "Direct link to Start Events") * **Generic**. Supported; an Activity will be created that visually resembles a standard Start Event. ![bpmn\_010](/documentation/assets/images/bpmn_010-5313680e860e63d93418eac1b36b128a.png) * **Message**. Partially supported; a Start Event Activity will be created with an outgoing Transition triggered by a Command. ![bpmn\_011](/documentation/assets/images/bpmn_011-8502107cf1f7d1aa9f788d8d611427bc.png) * **Timer**. Partially supported; a Start Event Activity will be created with an outgoing Transition triggered by a Timer. ![bpmn\_012](/documentation/assets/images/bpmn_012-dbf23cbfffacdd0dfdefcdce9b2c36f6.png) * **Signal**. Not supported; an Initial Activity with an outgoing automatic Transition will be created. * **Conditional**. Not supported; an Initial Activity with an outgoing automatic Transition will be created. If your BPMN diagram contains multiple Start Events, they will be merged into a single Start Event Activity in the Workflow Engine scheme, with corresponding Transitions emerging from it. ![bpmn\_013](/documentation/assets/images/bpmn_013-77e20080624d34abf1b56d8fbae0cf0a.png) ## End Events[​](#end-events "Direct link to End Events") * **Generic**. Supported; an Activity will be created that visually resembles a standard End Event. ![bpmn\_014](/documentation/assets/images/bpmn_014-8abc3c8bf4ddf64db0b280ebc65ca1a6.png) * **Terminate End Event**. Supported; an Activity will be created that visually resembles a standard Terminate End Event. ![bpmn\_015](/documentation/assets/images/bpmn_015-c7b2276c5afb503df27c03058887a21b.png) * **Message End Event**. Not supported; a Final Activity will be created. * **Escalation**. Not supported; a Final Activity will be created. * **Signal**. Not supported; a Final Activity will be created. * **Error**. Not supported; a Final Activity will be created. * **Compensation**. Not supported; a Final Activity will be created. ## Subprocesses[​](#subprocesses "Direct link to Subprocesses") All subprocesses will be converted into [Inline Activities](https://workflowengine.io/documentation/execution/scheme-inlining). The code of the inlined scheme will be taken from the subprocess element's name if it is provided; otherwise, the subprocess element's ID will be used. ![bpmn\_016](/documentation/assets/images/bpmn_016-e776d2cdb81a4dcd574a36d16f3553af.png) ## Pools[​](#pools "Direct link to Pools") A Pool will be loaded as a separate process scheme. Only the first scheme will be displayed in the designer. The scheme codes will correspond to the process name if it is provided; otherwise, the process ID will be used. ![bpmn\_017](/documentation/assets/images/bpmn_017-e40cbaf1036be50cc28c2c1c8f439eaf.png) ## Lanes[​](#lanes "Direct link to Lanes") Lanes will be ignored, as there is no equivalent method of grouping elements in Workflow Engine. ## Call Activities[​](#call-activities "Direct link to Call Activities") Not supported; they will be converted into standard Activities. ## Parallelism specifics[​](#parallelism-specifics "Direct link to Parallelism specifics") As previously discussed, parallelism in BPMN diagrams and Workflow Engine schemes differs. When importing schemes, implicit parallelism will always be converted into explicit parallelism. On the scheme, you will see dashed lines indicating the start of a subprocess and dash-dot lines indicating its completion. ![bpmn\_018](/documentation/assets/images/bpmn_018-5185e479354eb23ca7a170dcfdf93e8f.png) --- # Configuring and using BPMN plugin Now that we've familiarized ourselves with the terminology and how BPMN elements are transformed into Workflow Engine scheme elements, let's look at how to start using these features. ## Connecting BpmnPlugin[​](#connecting-bpmnplugin "Direct link to Connecting BpmnPlugin") First, connect the package with BpmnPlugin to the project where your **WorkflowRuntime** is initialized. ``` dotnet add package OptimaJet.Workflow.BpmnPlugin ``` Next, connect the **BpmnPlugin** to the Workflow Runtime. ``` var bpmnPlugin = new BpmnPlugin(); var runtime = new WorkflowRuntime() ... .WithPlugin(bpmnPlugin) ... .Start(); ``` After that, you need to restart your project, and you will be able to use BPMN import and BPMN elements in the designer. ## Using BPMN Elements in the scheme Designer[​](#using-bpmn-elements-in-the-scheme-designer "Direct link to Using BPMN Elements in the scheme Designer") After connecting the plugin, a new tab will appear on the **Elements** panel. ![bpmn\_100](/documentation/assets/images/bpmn_100-e3d3095214dd4fa0d9a251f6632415be.png) You can drag and drop BPMN elements from this panel and use them to build your scheme. The **Parallel Gateway** will be especially useful for creating simpler parallel processes compared to using the original Workflow Engine elements. ## Importing BPMN diagram into Workflow Engine[​](#importing-bpmn-diagram-into-workflow-engine "Direct link to Importing BPMN diagram into Workflow Engine") After connecting the plugin, a new menu item, **Upload BPMN**, will appear. ![bpmn\_101](/documentation/assets/images/bpmn_101-f8ada9c70faf3e224012dbd31490fdfb.png) Click on this menu item and upload a file containing the BPMN diagram. After that, a dialog box with import settings will appear. ![bpmn\_102](/documentation/assets/images/bpmn_102-7204675aace7620d1e3795180ae25c82.png) In this window, you will need the following elements: 1. **Main Scheme Code:** If the BPMN diagram contains multiple processes, the first one will be loaded with this scheme code, and the others will be loaded with codes corresponding to the process name, if provided; otherwise, the process ID will be used. By default, this field will contain the code of the scheme currently open in the designer. You can clear this code to allow the main scheme's code to be determined automatically. 2. **Checkbox "Overwrite previously uploaded schemes":** Check this box if you want the import to overwrite schemes that are already in the database. By default, it is unchecked to reduce the risk of overwriting modified and adjusted schemes. 3. To start the import process, click the **Upload** button. After the import is completed, the window will appear as follows. ![bpmn\_103](/documentation/assets/images/bpmn_103-6f9e4317b7e516490c25eb23e2af9c39.png) 1. You will have access to the import log, which you can review and analyze. 2. The import log can be copied and sent, for example, to technical support. 3. After analyzing the log, click the **Rerender** button to refresh the designer window and see the new scheme. If the scheme displayed in the designer has a different code from the one uploaded, or the main scheme was not uploaded, you will see a **Close** button instead of the **Rerender** button. ![bpmn\_104](/documentation/assets/images/bpmn_104-d245d7b97dac8082c5d527710136b113.png) In addition to the log, messages about potential issues during the import are added directly in the comments to the corresponding elements. ![bpmn\_105](/documentation/assets/images/bpmn_105-cfc0116713377ed4dc6343b9fc36875e.png) ## Customizing the import[​](#customizing-the-import "Direct link to Customizing the import") To customize the import process, you need to inherit the class responsible for importing the element you want to modify. Here's a list of classes you can inherit: * **TaskNodeVisitor** — Customizes how BPMN Tasks are transformed into Activities. * **GatewayNodeVisitor** — Customizes how BPMN Gateways are transformed into Activities. * **EventNodeVisitor** — Customizes how BPMN Events are transformed into Activities. * **SubprocessNodeVisitor** — Customizes how BPMN Subprocesses are transformed into Inline Activities. * **CallActivityNodeVisitor** — Customizes how BPMN Call Activities are transformed into Activities. * **TaskFlowVisitor** — Customizes how Sequence Flows from BPMN Tasks are transformed into Transitions. * **GatewayFlowVisitor** — Customizes how Sequence Flows from BPMN Gateways are transformed into Transitions. * **EventFlowVisitor** — Customizes how Sequence Flows from BPMN Events are transformed into Transitions. * **SubprocessFlowVisitor** — Customizes how Sequence Flows from BPMN Subprocesses are transformed into Transitions. * **CallActivityFlowVisitor** — Customizes how Sequence Flows from BPMN Call Activities are transformed into Transitions. * **ProcessVisitor** — Full customization of the BPMN process import. For example, if you want to implement a custom import for Call Activity, you need to inherit from **CallActivityNodeVisitor** and write your own implementation of the import. ``` public class CustomCallActivityNodeVisitor : CallActivityNodeVisitor { public override void AddCallActivity(TCallActivity callActivity, BpmnConverterContext context) { IActivityBuilder activityBuilder = context.Builder.CreateActivity(callActivity.Id); if (!string.IsNullOrWhiteSpace(callActivity.Name)) { activityBuilder .State(callActivity.Name) .EnableSetState(); } else { activityBuilder.NotState(); } } } ``` Then, you need to create an instance of **BpmnConverterBuilder**, specify your custom **CustomCallActivityNodeVisitor**, and use the **Build** method to create a new converter. ``` BpmnConverter customBpmnConverter = BpmnConverterBuilder .Create() .WithCustomCallActivityNodeVisitor(new CustomCallActivityNodeVisitor()) .Build(); ``` After that, you need to specify the created converter during the initialization of **BpmnPlugin**. ``` var bpmnPlugin = new BpmnPlugin(customBpmnConverter); var runtime = new WorkflowRuntime() ... .WithPlugin(bpmnPlugin) ... .Start(); ``` This way, you can partially modify the import behavior. Additionally, the **Build()** method accepts settings, whose values can be found in the API reference. --- ## [📄️ Workflow automation tools](https://workflowengine.io/documentation/guides/camuda-7-vs-workflow-engine) [Using the course payment process as an illustration, this post will compare Workflow Engine with Camunda 7, as a solution.](https://workflowengine.io/documentation/guides/camuda-7-vs-workflow-engine) --- # Creating a workflow schema using code info This functionality is available in Workflow Engine 13.3.0 and later. ## Overview[​](#overview "Direct link to Overview") Usually, process schemas are created using Designer. However, it can also be useful to use the [Builder steps](https://workflowengine.io/documentation/execution/build-steps) feature to change the workflow before launching. But sometimes, this is not sufficient. For example, you may want to convert a process schema from one format to the Workflow Engine format. In such cases, a mechanism for creating process schemas using code (programmatically) can come to your rescue. The process schema, in terms of Workflow Engine code, is called `ProcessDefinition`. The main class used to build a schema using code is called `ProcessDefinitionBuilder`. The [ProcessDefinitionBuilder](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.Builder.ProcessDefinitionBuilder) class implements a common pattern known as the Builder pattern, which is familiar to many developers. This pattern allows you to easily create a schema using a fluent style, making it more convenient. ## Building a schema using ProcessDefinitionBuilder[​](#building-a-schema-using-processdefinitionbuilder "Direct link to Building a schema using ProcessDefinitionBuilder") The creation of the schema is done in several steps: 1. Creation of an instance of the process builder, [IProcessDefinitionBuilder](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.BuilderIProcessDefinitionBuilder). 2. Creation of activities using [CreateActivity](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.Builder.ProcessDefinitionBuilder\&anchor=processdefinitionbuilder-createactivity-string-) method. 3. Creation of transitions using [CreateTransition](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.Builder.ProcessDefinitionBuilder\&anchor=processdefinitionbuilder-createtransition-string--activitydefinition--activitydefinition-) method. 4. Creation of other workflow elements (see [CreateCommand](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.Builder.ProcessDefinitionBuilder\&anchor=processdefinitionbuilder-createcommand-string-), [CreateTimer](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.Builder.ProcessDefinitionBuilder\&anchor=processdefinitionbuilder-createtimer-string-), [CreateActor](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.Builder.ProcessDefinitionBuilder\&anchor=processdefinitionbuilder-createactor-string--string-), etc.). 5. Creation of XML with a schema. ### Creating an extremely simple scheme[​](#creating-an-extremely-simple-scheme "Direct link to Creating an extremely simple scheme") Start by creating a builder: ``` IProcessDefinitionBuilder builder = ProcessDefinitionBuilder.Create("SimpleSchema"); ``` Then, create two activities: ``` builder.CreateActivity("StartActivity") .Initial() .State("StartState") .EnableSetState() .SetX(300) .SetY(100) .Ref(out ActivityDefinition startActivity); builder.CreateActivity("FinalActivity") .Final() .State("FinalState") .EnableSetState() .SetX(600) .SetY(100) .Ref(out ActivityDefinition finalActivity); ``` Next, create a command: ``` builder.CreateCommand("go") .Ref(out CommandDefinition goCommand); ``` Finally, create a transition between the activities: ``` builder.CreateTransition("InitialTransition", startActivity, finalActivity) .Direct() .TriggeredByCommand(goCommand) .Ref(out TransitionDefinition initialTransition); ``` Good, the schema is ready. You can now get the XML representation using [ProcessDefinition](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.Builder.ProcessDefinitionBuilder\&anchor=processdefinitionbuilder-processdefinition): ``` string xml = builder.ProcessDefinition.Serialize(); ``` Here is the content of the resulting XML: SimpleSchema.xml ``` ``` ![A simple process schema generated programmatically](/documentation/assets/images/creating-a-workflow-schema-using-code-1-806a384bb2375b056abe8aa04483508b.png "A simple process schema generated programmatically") In fact, this is a simple and understandable scheme with two activities and a transition between them. The names of the methods speak for themselves, and you can easily understand what each method does. For a complete list of methods, refer to the [API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.Builder). ### Creating a vacation request process schema[​](#creating-a-vacation-request-process-schema "Direct link to Creating a vacation request process schema") Below is an example of building a schema from a [demo](https://demo.workflowengine.io/designer) using the Builder. Despite the fact that this code is quite long, it is well commented and easy to understand. ``` IProcessDefinitionBuilder processDefinitionBuilder = ProcessDefinitionBuilder.Create("VacationRequest"); processDefinitionBuilder // create a parameter "Comment" .CreateParameter("Comment", typeof(string), ParameterPurpose.Persistence) .InitialValue("") .Ref(out ParameterDefinition commentParameter) // create a command "StartSigning" .CreateCommand("StartSigning").CreateCommandParameter("Comment", commentParameter).NotRequired() .Ref(out CommandDefinition startSigningCommand) // create a command "Approve" .CreateCommand("Approve").CreateCommandParameter("Comment", commentParameter).NotRequired() .Ref(out CommandDefinition approveCommand) // create a command "Reject" .CreateCommand("Reject").CreateCommandParameter("Comment", commentParameter).NotRequired() .Ref(out CommandDefinition rejectCommand) // create a command "Paid" .CreateCommand("Paid").CreateCommandParameter("Comment", commentParameter).NotRequired().Ref(out CommandDefinition paidCommand) // create a timer "SendToBigBoss" with an interval of 10 minutes .CreateTimer("SendToBigBoss").Interval(TimeSpan.FromMinutes(10)).Overridable().Ref(out TimerDefinition timer); // create actors processDefinitionBuilder .CreateActor("BigBoss", "CheckRole") .Value("Big Boss") .Ref(out ActorDefinition bigBossActor) .CreateActor("Accountant", "CheckRole") .Value("Accountant") .Ref(out ActorDefinition accountantActor) .CreateActor("Manager", "Manager") .Value("Manager") .Ref(out ActorDefinition managerActor) .CreateActor("Author", "Author") .Value("Author") .Ref(out ActorDefinition authorActor); // create activities processDefinitionBuilder // create activity "VacationRequestCreated" .CreateActivity("VacationRequestCreated") .State("VacationRequestCreated") .Initial() .EnableSetState() .EnableAutoSchemeUpdate() .SetX(320).SetY(220) .Ref(out ActivityDefinition vacationRequestCreatedActivity) // create activity "ManagerSigning" .CreateActivity("ManagerSigning") .State("ManagerSigning") .EnableSetState() .EnableAutoSchemeUpdate() .SetX(660).SetY(220) .Ref(out ActivityDefinition managerSigningActivity) // create activity "BigBossSigning" .CreateActivity("BigBossSigning") .State("BigBossSigning") .EnableSetState() .EnableAutoSchemeUpdate() .SetX(1000).SetY(220) .Ref(out ActivityDefinition bigBossSigningActivity) // create activity "AccountingReview" .CreateActivity("AccountingReview") .State("AccountingReview") .EnableSetState() .EnableAutoSchemeUpdate() .SetX(1000).SetY(380) .Ref(out ActivityDefinition accountingReviewActivity) // create activity "RequestApproved" .CreateActivity("RequestApproved") .State("RequestApproved") .Final() .EnableSetState() .EnableAutoSchemeUpdate() .SetX(1280).SetY(380) .Ref(out ActivityDefinition requestApprovedActivity); // create transitions processDefinitionBuilder // create transition from activity "ManagerSigning" to activity "VacationRequestCreated" .CreateTransition("ManagerSigning_Draft_1", managerSigningActivity, vacationRequestCreatedActivity) .Reverse() .TriggeredByCommand(rejectCommand) .CreateRestriction(managerActor, RestrictionType.Allow) .ConcatAllowByAnd() .ConcatRestrictByAnd() .Conditional() .ConcatConditionsByAnd() .Always() .SetX(575).SetY(236) // create transition from activity "BigBossSigning" to activity "AccountingReview" .CreateTransition("BigBossSigning_Activity_1_1", bigBossSigningActivity, accountingReviewActivity) .Direct() .TriggeredByCommand(approveCommand) .CreateRestriction(bigBossActor, RestrictionType.Allow) .ConcatAllowByAnd() .ConcatRestrictByAnd() .Conditional() .ConcatConditionsByAnd() .Always() // create transition from activity "ManagerSigning" to activity "AccountingReview" .CreateTransition("ManagerSigning_Approved_1", managerSigningActivity, accountingReviewActivity) .Direct() .TriggeredByCommand(approveCommand) .CreateRestriction(managerActor, RestrictionType.Allow) .ConcatAllowByAnd() .ConcatRestrictByAnd() .Conditional() .ConcatConditionsByAnd() .Always() .SetX(761).SetY(334) // create transition from activity "ManagerSigning" to activity "BigBossSigning" .CreateTransition("ManagerSigning_BigBossSigning_1", managerSigningActivity, bigBossSigningActivity) .Direct() .TriggeredByCommand(approveCommand) .CreateRestriction(managerActor, RestrictionType.Allow) .ConcatAllowByAnd() .ConcatRestrictByAnd() .Conditional() .CreateExpression("@sum > 100") .ConcatConditionsByAnd() .SetX(905).SetY(238) // create transition from activity "VacationRequestCreated" to activity "ManagerSigning" .CreateTransition("Draft_ManagerSigning_1", vacationRequestCreatedActivity, managerSigningActivity) .Direct() .TriggeredByCommand(startSigningCommand) .CreateRestriction(authorActor, RestrictionType.Allow) .ConcatAllowByAnd() .ConcatRestrictByAnd() .Conditional() .ConcatConditionsByAnd() .Always() .SetX(572).SetY(268) // create transition from activity "BigBossSigning" to activity "ManagerSigning" .CreateTransition("BigBossSigning_ManagerSigning_1", bigBossSigningActivity, managerSigningActivity) .Reverse() .TriggeredByCommand(rejectCommand) .CreateRestriction(bigBossActor, RestrictionType.Allow) .ConcatAllowByAnd() .ConcatRestrictByAnd() .Conditional() .ConcatConditionsByAnd() .Always() .SetX(902).SetY(268) // create transition from activity "ManagerSigning" to activity "BigBossSigning" .CreateTransition("ManagerSigning_BigBossSigning_2", managerSigningActivity, bigBossSigningActivity) .DirectionNotSpecified() .TriggeredByTimer(timer) .Conditional() .ConcatConditionsByAnd() .Always() .SetX(903).SetY(122) // create transition from activity "AccountingReview" to activity "RequestApproved" .CreateTransition("Accountant_Activity_1_1", accountingReviewActivity, requestApprovedActivity) .Direct() .TriggeredByCommand(paidCommand) .CreateRestriction(accountantActor, RestrictionType.Allow) .ConcatAllowByAnd() .ConcatRestrictByAnd() .Conditional() .ConcatConditionsByAnd() .Always() // create transition from activity "AccountingReview" to activity "ManagerSigning" .CreateTransition("Accountant_ManagerSigning_1", accountingReviewActivity, managerSigningActivity) .Reverse() .TriggeredByCommand(rejectCommand) .CreateRestriction(accountantActor, RestrictionType.Allow) .ConcatAllowByAnd() .ConcatRestrictByAnd() .Conditional() .ConcatConditionsByAnd() .Always() .SetX(702).SetY(420); string xml = processDefinitionBuilder.ProcessDefinition.Serialize(); ``` And here is the resulting XML: VacationRequest.xml ``` 100]]> ``` Well, the picture is for clarity: ![The schema of the vacation request process, generated programmatically](/documentation/assets/images/creating-a-workflow-schema-using-code-2-c66ee1af464194e3b91442105c3e1b6c.png "The schema of the vacation request process, generated programmatically") ## Conclusion[​](#conclusion "Direct link to Conclusion") In this guide, we have looked at how to create a workflow schema using code (programmatically). The API allows you to make any process schemas, and is in no way inferior to the capabilities of a visual Designer. If necessary, find the right method to create a schema element - use the documentation from the [API Reference](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.Builder). You can find more information in this [article](https://workflowengine.io/documentation/scheme/programmatic-schema-builder). --- # MS SQL Server Relational Database This section describes the Workflow Engine database objects. All objects are inside the `dbo` schema. ## Tables[​](#tables "Direct link to Tables") 1. [WorkflowApprovalHistory](https://workflowengine.io/documentation/db-entities/tables/WorkflowApprovalHistory) 2. [WorkflowGlobalParameter](https://workflowengine.io/documentation/db-entities/tables/WorkflowGlobalParameter) 3. [WorkflowInbox](https://workflowengine.io/documentation/db-entities/tables/WorkflowInbox) 4. [WorkflowProcessAssignment](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessAssignment) 5. [WorkflowProcessInstance](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstance) 6. [WorkflowProcessInstancePersistence](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstancePersistence) 7. [WorkflowProcessInstanceStatus](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstanceStatus) 8. [WorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessScheme) 9. [WorkflowProcessTimer](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessTimer) 10. [WorkflowProcessTransitionHistory](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessTransitionHistory) 11. [WorkflowRuntime](https://workflowengine.io/documentation/db-entities/tables/WorkflowRuntime) 12. [WorkflowScheme](https://workflowengine.io/documentation/db-entities/tables/WorkflowScheme) 13. [WorkflowSync](https://workflowengine.io/documentation/db-entities/tables/WorkflowSync) ## Procedures[​](#procedures "Direct link to Procedures") 1. [DropUnusedWorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/procedures/DropUnusedWorkflowProcessScheme) 2. [DropWorkflowInbox](https://workflowengine.io/documentation/db-entities/procedures/DropWorkflowInbox) 3. [spWorkflowProcessResetRunningStatus](https://workflowengine.io/documentation/db-entities/procedures/spWorkflowProcessResetRunningStatus) --- # Procedures 1. [DropUnusedWorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/procedures/DropUnusedWorkflowProcessScheme) 2. [DropWorkflowInbox](https://workflowengine.io/documentation/db-entities/procedures/DropWorkflowInbox) 3. [spWorkflowProcessResetRunningStatus](https://workflowengine.io/documentation/db-entities/procedures/spWorkflowProcessResetRunningStatus) --- # DropUnusedWorkflowProcessScheme ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ------------------------------- | ---------- | | dbo | DropUnusedWorkflowProcessScheme | Procedures | ## Uses[​](#uses "Direct link to Uses") | Name | Related to | | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | *DropUnusedWorkflowProcessScheme* | | | | [WorkflowProcessInstance](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstance) | | | [WorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessScheme) | ## Scripts[​](#scripts "Direct link to Scripts") ``` CREATE PROCEDURE [DropUnusedWorkflowProcessScheme] AS BEGIN DELETE wps FROM WorkflowProcessScheme AS wps WHERE wps.IsObsolete = 1 AND NOT EXISTS (SELECT * FROM WorkflowProcessInstance wpi WHERE wpi.SchemeId = wps.Id ) RETURN (SELECT COUNT(*) FROM WorkflowProcessInstance wpi LEFT OUTER JOIN WorkflowProcessScheme wps ON wpi.SchemeId = wps.Id WHERE wps.Id IS NULL) END ``` --- # DropWorkflowInbox ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ----------------- | ---------- | | dbo | DropWorkflowInbox | Procedures | ## Input/Output[​](#inputoutput "Direct link to Input/Output") | Mode | Name | Data Type | | ---- | --------- | ----------------- | | *IN* | processId | Unique identifier | ## Uses[​](#uses "Direct link to Uses") | Name | Related to | | ------------------- | ------------------------------------------------------------------------------------------------------- | | *DropWorkflowInbox* | | | | [WorkflowInbox](https://workflowengine.io/documentation/db-entities/tables/WorkflowInbox) | ## Scripts[​](#scripts "Direct link to Scripts") ``` CREATE PROCEDURE [DropWorkflowInbox] @processId uniqueidentifier AS BEGIN BEGIN TRAN DELETE FROM dbo.WorkflowInbox WHERE ProcessId = @processId COMMIT TRAN END ``` --- # spWorkflowProcessResetRunningStatus ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ----------------------------------- | ---------- | | dbo | spWorkflowProcessResetRunningStatus | Procedures | ## Uses[​](#uses "Direct link to Uses") | Name | Related to | | ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | *spWorkflowProcessResetRunningStatus* | | | | [WorkflowProcessInstanceStatus](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstanceStatus) | ## Scripts[​](#scripts "Direct link to Scripts") ``` CREATE PROCEDURE [spWorkflowProcessResetRunningStatus] AS BEGIN UPDATE [WorkflowProcessInstanceStatus] SET [WorkflowProcessInstanceStatus].[Status] = 2 WHERE [WorkflowProcessInstanceStatus].[Status] = 1 END ``` --- # Tables 1. [WorkflowApprovalHistory](https://workflowengine.io/documentation/db-entities/tables/WorkflowApprovalHistory) 2. [WorkflowGlobalParameter](https://workflowengine.io/documentation/db-entities/tables/WorkflowGlobalParameter) 3. [WorkflowInbox](https://workflowengine.io/documentation/db-entities/tables/WorkflowInbox) 4. [WorkflowProcessAssignment](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessAssignment) 5. [WorkflowProcessInstance](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstance) 6. [WorkflowProcessInstancePersistence](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstancePersistence) 7. [WorkflowProcessInstanceStatus](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstanceStatus) 8. [WorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessScheme) 9. [WorkflowProcessTimer](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessTimer) 10. [WorkflowProcessTransitionHistory](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessTransitionHistory) 11. [WorkflowRuntime](https://workflowengine.io/documentation/db-entities/tables/WorkflowRuntime) 12. [WorkflowScheme](https://workflowengine.io/documentation/db-entities/tables/WorkflowScheme) 13. [WorkflowSync](https://workflowengine.io/documentation/db-entities/tables/WorkflowSync) --- # WorkflowApprovalHistory ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/TOvDIyD0443l-ok6djf2eItg8OJGO0y6iHGQqElOdSR1_QYpavOW_dS9HYoertblviqgAOgstXaAojh1w37Awy75StayIZoXk-EaKReHO0AQB-3TJAjr1ctWjvQ0RRztO1B8y-rzKPZPp4ot4lUKKcN_zXeA9AXaYy9KzYlSkm5EAFiNbF7o-cO2URuf2bCw5yzawtZ1OVnGFdqBjM18h1n3pPuoiAYax6d0koYQmJCt77GOLO6LqUqKbIwhY_doQcBMb9G3zWV_CMhXfY7PeFyDXyHjz9w2edINni_NO3xCYe9jlVi4) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ----------------------- | ----- | | dbo | WorkflowApprovalHistory | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Null | Reference | | ------------------ | --- | ----------------- | ---- | --------- | | *ID* | + | Unique identifier | | | | *ProcessId* | | Unique identifier | | | | *IdentityId* | | nvarchar(256) | Yes | | | *AllowedTo* | | nvarchar(MAX) | Yes | | | *TransitionTime* | | datetime | Yes | | | *Sort* | | bigint | Yes | | | *InitialState* | | nvarchar(1024) | | | | *DestinationState* | | nvarchar(1024) | | | | *TriggerName* | | nvarchar(1024) | Yes | | | *Commentary* | | nvarchar(MAX) | Yes | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | --------------------------- | ------- | | PK\_WorkflowApprovalHistory | ID | --- # WorkflowGlobalParameter ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/LSl12e90403G-tx5u4a5GGiYP163A28A3r5TfnnfQNUjSRUGwD_3IzJvyShE8Jjlj23hbEiXW4FBrqQtpwLkJwWhP3JaY0F03YZDu2NYrJm7RzNT4wXwY8qY1YchTL68917nhhzH3XOUoESBSZXE9n58kHWytgBvmsmq_U8UjVz523UpOoJUeYHRUwC_) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ----------------------- | ----- | | dbo | WorkflowGlobalParameter | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Reference | | ------- | --- | ----------------- | --------- | | *ID* | + | Unique identifier | | | *Type* | | nvarchar(306 | | | *Name* | | nvarchar(128) | | | *Value* | | nvarchar(MAX) | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | --------------------------- | ---------- | | PK\_WorkflowGlobalParameter | ID | | IX\_Type\_Name\_Clustered | Type, Name | --- # WorkflowInbox ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/RSqz2y8m483X_Nx5uQG5GGKTIYaMNOe8RhgUlMiD9XTCKZyG_xk8kBa_B-yo9WgfTnP4aqaF7EnzEBVMtsez-li0AQ9CflY4h5xdsAkvz8A6Fxer4h0eTfkoXF4OibtmZSHOyt_NYKgW95oMKFDtzx6ALmhDYS9mDb-C42fcezsQakJ8b2GP9r1ToLWwMbbvvqWvvgY_R5iTHl22fIZtphu1) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ------------- | ----- | | dbo | WorkflowInbox | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Null | Attributes | Reference | | ------------------- | --- | ----------------- | ---- | ------------------ | --------- | | *ID* | + | Unique identifier | | | | | *EmployeeId* | | Unique identifier | | | | | *IdentityId* | | nvarchar(256) | | | | | *AddingDate* | | datetime | | Default: getdate() | | | *AvailableCommands* | | nvarchar(MAX) | | Default: '' | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | ----------------- | ------- | | PK\_WorkflowInbox | ID | ## Used by[​](#used-by "Direct link to Used by") | Name | Related to | | --------------- | ------------------------------------------------------------------------------------------------------------------- | | *WorkflowInbox* | | | | [DropWorkflowInbox](https://workflowengine.io/documentation/db-entities/procedures/DropWorkflowInbox) | --- # WorkflowProcessAssignment ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/TP1HIyCm4CVVyocEFTd1O0uLAQMij0f5d8CDz3LhRjrXcypSTIhYTvUkHSlKbq3kTxz_BZTdqLwQkb9eXUGTHe_EFsyhzxhqha3cX9bAMwCL685cmEavVAXndeNGM7ff4CYqwfRGGnGjx-9OJIRgHqkTmH3iGVjYf_tPR7fn7QXndvwRlsDAjEYre8bZjT3rCE0-UGfKXbnusWiv6uA5di4HGXGjubYjH4l3xOc3bbxNWgb7tVb62mhL2CVwgbt8eDYbjUIMBF7kDq9jAhAuTl-etpnrzRx2TfwJbfitB1fnVZZexF8g0FMmOVG7z7pwXzvSwv9FbjEJd9D2w80XR4XKpXcsJvlk-gdcQ4rJLry0) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ------------------------- | ----- | | dbo | WorkflowProcessAssignment | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Null | Reference | | -------------------- | --- | ----------------- | ---- | --------- | | *ID* | + | Unique identifier | | | | *AssignmentCode* | | nvarchar(2048) | | | | *ProcessId* | | Unique identifier | | | | *Name* | | nvarchar(MAX) | | | | *Description* | | nvarchar(MAX) | Yes | | | *StatusState* | | nvarchar(MAX) | | | | *DateCreation* | | datetime | | | | *DateStart* | | datetime | Yes | | | *DateFinish* | | datetime | Yes | | | *DeadlineToStart* | | datetime | Yes | | | *DeadlineToComplete* | | datetime | Yes | | | *Executor* | | nvarchar(256) | | | | *Observers* | | nvarchar(MAX) | Yes | | | *Tags* | | nvarchar(MAX) | Yes | | | *IsActive* | | bit | | | | *IsDeleted* | | bit | | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | ----------------------------- | ------- | | PK\_WorkflowProcessAssignment | ID | --- # WorkflowProcessInstance ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/ZP9FIyD04CNl-oc6dbGijEAfXD1Y48fQGbFGwxYPjel9hCvE8aNyxf9gg9MsvZo_UU_Ddt5G5AtAmX2hqmtq7hsyB0l_degt5CAKWo9RwW46eC4GFip5D1b1nUwj8d1vqxTq9115wLqSctxVP8fAConf10mrYbsZd3rCdiuXYcPnR2PMNUrqSn0ncLrJITFyiCUAc0ILyajef5AXslagR2sFsEqnjruI9sIr8podcYJy9zrEr17h6k8NVp87c1I5MDkRTTlTtFlE7N5i5iJ8sf2x2CF1rNKREci-oV5g8SZ1gVDyPBgiUdxzjZtn8pT2s6WasxlcgAIk933t67JdyAVwiunFCoREgxBu0W00) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ----------------------- | ----- | | dbo | WorkflowProcessInstance | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Null | Attributes | Reference | | ---------------------------- | --- | ----------------- | ---- | ------------------ | --------- | | *ID* | + | Unique identifier | | | | | *StateName* | | nvarchar(MAX) | Yes | | | | *ActivityName* | | nvarchar(MAX) | | | | | *SchemeId* | | Unique identifier | Yes | | | | *PreviousState* | | nvarchar(MAX) | Yes | | | | *PreviousStateForDirect* | | nvarchar(MAX) | Yes | | | | *PreviousStateForReverse* | | nvarchar(MAX) | Yes | | | | *PreviousActivity* | | nvarchar(MAX) | Yes | | | | *PreviousActivityForDirect* | | nvarchar(MAX) | Yes | | | | *PreviousActivityForReverse* | | nvarchar(MAX) | Yes | | | | *ParentProcessId* | | Unique identifier | Yes | | | | *RootProcessId* | | Unique identifier | | | | | *TenantId* | | nvarchar(1024) | Yes | | | | *StartingTransition* | | nvarchar(MAX) | Yes | | | | *SubprocessName* | | nvarchar(MAX) | Yes | | | | *CreationDate* | | datetime | | Default: getdate() | | | *LastTransitionDate* | | datetime | Yes | | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | --------------------------- | ------- | | PK\_WorkflowProcessInstance | ID | ## Used by[​](#used-by "Direct link to Used by") | Name | Related to | | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | *WorkflowProcessInstance* | | | | [DropUnusedWorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/procedures/DropUnusedWorkflowProcessScheme) | --- # WorkflowProcessInstancePersistence ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/RSon2i8m483X_PxYwAI5WgwbX0ekHPHCwdeaLmqcALuIHSHt5mSdNNz-lZvbabo21uxPvGTMXqakev_kMYR3AGqnPOg6DKjoANCqN24bvCKIdr0FwnPBTDV2wEo761qBTftUA0LD0_MNiV-_4qSMocoL0ar2WJFBZWAt6F56Oiuai-tgE8Sz-V9J4LxGSxGb-3S0) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ---------------------------------- | ----- | | dbo | WorkflowProcessInstancePersistence | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Reference | | --------------- | --- | ----------------- | --------- | | *ID* | + | Unique identifier | | | *ProcessId* | | Unique identifier | | | *ParameterName* | | nvarchar(MAX) | | | *Value* | | nvarchar(MAX) | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | -------------------------------------- | ------- | | PK\_WorkflowProcessInstancePersistence | ID | --- # WorkflowProcessInstanceStatus ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/RSkn2i8m403G_RxYwAH2eO8kePGEBaM7OWNda5mrjBrWSb6A-EzINDtVgwFe86aQWLYSp9XTVHZwqR_Qu0t5s70KpOOwqP9YXZeY5LjymwOvA4pi7edGsMNtZWAMPNkiAiXp-0s5udXsB73oPlW_RiGKj92jAZWd5ZTHOnKoFdKmTnrMktsnHkX8BcuYXLOBBGZX0pMnJTFu1G00) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ----------------------------- | ----- | | dbo | WorkflowProcessInstanceStatus | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Reference | | ----------- | --- | ----------------- | --------- | | *ID* | + | Unique identifier | | | *Status* | | tinyint | | | *Lock* | | Unique identifier | | | *RuntimeId* | | nvarchar(450) | | | *SetTime* | | datetime | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | --------------------------------- | ------- | | PK\_WorkflowProcessInstanceStatus | ID | ## Used by[​](#used-by "Direct link to Used by") | Name | Related to | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | *WorkflowProcessInstanceStatus* | | | | [spWorkflowProcessResetRunningStatus](https://workflowengine.io/documentation/db-entities/procedures/spWorkflowProcessResetRunningStatus) | --- # WorkflowProcessScheme ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/RP11Qy9048Nlyoi6JreGLB2741Q3Ne9eHGljTSsEEhZPmTs9jaZ_Umbwy93ZFBxlyPXPKXkrgJrGKDPVx7r9F1-yt3PHAaffLvsefXxQX3GQuntUoaM6JU1BGyYkjGvC4VDyipG6XaDu61a6fHzzNdDnRO9N6wkJZTYVJDy726Nws2Vnf9JXdXMs8jefFFWyNnk3-8ALhdlBsXWel9SRkQ9Ilh8ofTU-LV7zx8DT-m4EnyzeGs9b2TqWmX_CABYczly0) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | --------------------- | ----- | | dbo | WorkflowProcessScheme | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Null | Attributes | Reference | | -------------------- | --- | ----------------- | ---- | ---------- | --------- | | *ID* | + | Unique identifier | | | | | *Scheme* | | ntext | | | | | *SchemeCode* | | nvarchar(256) | | | | | *IsObsolete* | | Bit | | Default: 0 | | | *RootSchemeCode* | | nvarchar(256) | | | | | *RootSchemeId* | | Unique identifier | | | | | *AllowedActivities* | | nvarchar(MAX) | | | | | *StartingTransition* | | nvarchar(MAX) | | | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | ------------------------- | ------- | | PK\_WorkflowProcessScheme | ID | ## Used by[​](#used-by "Direct link to Used by") | Name | Related to | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | *WorkflowProcessScheme* | | | | [DropUnusedWorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/procedures/DropUnusedWorkflowProcessScheme) | --- # WorkflowProcessTimer ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/bSwx2i9044RXVfvYiDA0e6sGeA1544L4q7RD_iR5x2vEPhqWlhi4B6tipmTdsgeHJRuX17NwvDu-okNKnFj6OeMstJaFwR5f6QCnloWhvpcduAu9x6o7JWx2aybcMHGq75BsXQNztTK84AEmHK7R6FMFV6qySWvyCrATZN1_DJiCcDPuwEA1AgcBOMuKtNFErYZKUL1PXoZ8-UYKtZH5iCat7m00) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | -------------------- | ----- | | dbo | WorkflowProcessTimer | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Reference | | ----------------------- | --- | ----------------- | --------- | | *ID* | + | Unique identifier | | | *ProcessId* | | Unique identifier | | | *RootProcessId* | | Unique identifier | | | *Name* | | nvarchar(MAX) | | | *NextExecutionDateTime* | | datetime | | | *Ignore* | | Bit | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | ------------------------ | ------- | | PK\_WorkflowProcessTimer | ID | --- # WorkflowProcessTransitionHistory ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/ZP9DImCn48Rl-HL3JxPGK44FPGajrk8Yba8Nz1etqtLmDy79fBg8_rtILfH-Ufh3--ITXo7388ObDhL29oGjT1uzlovg_pvZNs881HiNICYxEmhYkUs02O3d5_2fUlbu0D7HMqGWcmeMX0nPDhlNMlNxghSfoUr-haA7R0Ijrkhs0yieddExDabl72mDbo-6powlhhkGPLEjrQZy3_LJDpKD7Yiwd8EQi6z6fT2IfDtc7aPFNL25FvNFnGZk3TSIl_SjA47M20er-2UugKq8gvFjT83AmuISgIcW7S0poKhwz5AgAkGZn3nzYqDkE-hZo2RDP52HamtmfOReR6pgRm00) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | -------------------------------- | ----- | | dbo | WorkflowProcessTransitionHistory | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Null | Reference | | ---------------------- | --- | ----------------- | ---- | --------- | | *ID* | + | Unique identifier | | | | *ProcessId* | | Unique identifier | | | | *ExecutorIdentityId* | | nvarchar(256) | Yes | | | *ActorIdentityId* | | nvarchar(256) | Yes | | | *ExecutorName* | | nvarchar(256) | Yes | | | *ActorName* | | nvarchar(256) | Yes | | | *FromActivityName* | | nvarchar(MAX) | | | | *ToActivityName* | | nvarchar(MAX) | | | | *ToStateName* | | nvarchar(MAX) | Yes | | | *TransitionTime* | | datetime | | | | *TransitionClassifier* | | nvarchar(MAX) | | | | *IsFinalised* | | Bit | | | | *FromStateName* | | nvarchar(MAX) | Yes | | | *TriggerName* | | nvarchar(MAX) | Yes | | | *StartTransitionTime* | | datetime | Yes | | | *TransitionDuration* | | bigint | Yes | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | ------------------------------------ | ------- | | PK\_WorkflowProcessTransitionHistory | ID | --- # WorkflowRuntime ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/TOx1IWD144JlynLDJYe48kXbMOPu54EGhE2vsQaaJMPxiQTdDOZ_BWiAYdYfGxtYKUlYR5w77A0kVgR5ysYdVHvVTrLT1Yo82s5rJU_XwgkwJmqfJMpzaUtYvdPrIMtx-11ZM2x3PknF3LMLbmgID5lt0gEsFK1Xx4WnXixPQsd8HS-Y7dOeFXhibvg-tTiOmnPl_YG3R8w64ZlcBpze1vkan_-Z3HU_opAXauDo_iC_mXgQwf0_0G00) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | --------------- | ----- | | dbo | WorkflowRuntime | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Null | Reference | | ---------------------- | --- | ----------------- | ---- | --------- | | *RuntimeId* | + | nvarchar(450) | | | | *Lock* | | Unique identifier | | | | *Status* | | tinyint | | | | *RestorerId* | | nvarchar(450) | Yes | | | *NextTimerTime* | | datetime | Yes | | | *NextServiceTimerTime* | | datetime | Yes | | | *LastAliveSignal* | | datetime | Yes | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | ------------------- | ------- | | PK\_WorkflowRuntime | ID | --- # WorkflowScheme ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/SoWkIImgAStDuKhDAyaigLHG2iyloaxBoIyFJiv8pKrLKaWiLaWrC5Ievj9opaz9jLB8KoXBB4hEI2pIC38rqrImiGdmjhFZqjNbqeBeG5RXwnYXgS3bd9ZdbEgPbvEPbvfYfP2KMS85vK0q52FfK81eiR7nixFZ2abCnobNo-MGcfTIcfi30000) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | -------------- | ----- | | dbo | WorkflowScheme | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Null | Attributes | Reference | | ---------------- | --- | ------------- | ---- | ---------- | --------- | | *Code* | + | nvarchar(256) | | | | | *Scheme* | | nvarchar(MAX) | | | | | *CanBeInlined* | | Bit | | Default: 0 | | | *InlinedSchemes* | | nvarchar(MAX) | Yes | | | | *Tags* | | nvarchar(MAX) | Yes | | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | ------------------ | ------- | | PK\_WorkflowScheme | Code | --- # WorkflowSync ## Entity[​](#entity "Direct link to Entity") ![ERD](https://www.plantuml.com/plantuml/png/5Sp12i8m303GUxuYxAJ2O89UHYcxJqJmu3cqcPQrAMQjCiH_5zy7l64ffAMc22mbb1MRMzPvYlbzNSKrI0jojySFxCwKk4V15wbxa6uEnswBnbn6Qw5juPJTt6ELyAoCmV-tAR2YCNSMLYhihOKl32o-flW3) ## Reference[​](#reference "Direct link to Reference") | Schema | Name | Type | | ------ | ------------ | ----- | | dbo | WorkflowSync | Table | ## Columns[​](#columns "Direct link to Columns") | Name | Key | Data Type | Reference | | ------ | --- | ----------------- | --------- | | *Name* | + | nvarchar(450) | | | *Lock* | | Unique identifier | | ## Unique keys[​](#unique-keys "Direct link to Unique keys") | Key name | Columns | | ---------------- | ------- | | PK\_WorkflowSync | Name | --- # Demo application In this section is introduced a simple example which describes a *Vacation Approval Process* in our Demo Application that is available [here](https://demo.workflowengine.io/Designer). Furthermore, a layered structured diagram is included which presents a general view of our architecture solution by implementing Workflow Engine components and the Designer considering the example presented. ![Archimate ](https://www.plantuml.com/plantuml/png/dLJBRjim43oNNx58UogW52YGYs0BaIsdBT00XkF4vxMqaPbGf41I2TB7lnUaAEk1n0XwvUxCFcOvbyQYjaqbc7daiaQD5MZABSfIq6osfmC-SQMJ17wp3reribWgBKd3rmlsbp5JQsu9FktNMkLa33oWZb7d1rwXfI_xnd19naHr6-xpqzmg_LOskk0n5vTA2DmhZPOhES5WNGkU-qYK3_Dw-0tf9vxJ2Q1fCuOLgrf9alPajP3JmpAqU09HeCL8xHyexp4srPRVOpwjP2a_I2LK-H9XoEWn6Rqoq01H-3XZHo7XR5NLGhqGdG4QM5t3R1OqI9AHoXyNGOGX-0xR2I2jQwsUK7YMkzIp1ENVv66hQ_WS5UfPHWuIWr-kuLwaOPNls-kV5v2HuQKaxKiiCbTYV03JGcc_uI5RnmFf-eVdodoxtJMyasLtjO9D8oslsirjRaD3GVltDhC0jpXUXBcozN_Hx9H-l1Vg6RQu5sGyrMxhgTpfdM9XYmoYfewILWE_1LcopUrhh-wvaonRJoNBKeW4tTieSVgCkCxT-_cVn2qxXWsXuB_Q-gxDDjeofhdtWrUdMycIotRVkoklsl6NkB6snqCVeh-H98qYMZUwLePyLwHXAHfZmzPlbfwh_JqJ9kOgdJUrRtuzQVvc2P7cvS6DlBjA1uiIL98im0TPHfMA8NK_qb9k6qrGS2mrLcm-dy_PySj1t5aTj5Ot-4Q3M6UQ0Jpv1X0Fl0N6tZUuyt5QvtIZuvioTRxMdTKuxbmCWek5pdfv8Evj28x6rIzz50ysHR9eryZO9ScYgSG_) Take into account In the public *Demo* this process example can not be changed. ## 1. Process automation in Workflow Engine[​](#process-automation "Direct link to 1. Process automation in Workflow Engine") The workflow process is modeled easily in the Designer Canvas by creating a process scheme. The process main components are: `Activities` and `Transitions`. The Activities define the sequence which actions are completed, and the Transitions allow passing from one state to another, and they can be triggered by timers or commands with some conditions. Just it is needed to drag and drop these scheme components to model any desired process. Then, the modeled process can be automated based on the workflow scheme through the Workflow Engine solution. The following simple steps are needed to add an activity in the process scheme: 1. Open the Elements panel. 2. Find the type of activity in the elements list or use Search. 3. Drag the element to the canvas. 4. Click on the Activity element in 'Create Transition' arrow to include a `Transition`. ![image1](/documentation/assets/images/demo-1-f06822e3b4262d29be27832da7871ebc.png) Once clicking on the `Transition`, in the sections: *Trigger* and *Restriction*, the `Commands` and `Actors` should be set as is indicated in the picture: ![restriction](/documentation/assets/images/demo-restriction-69b8a26e320bcbfa05bc77de3683c916.png) The `Commands` and `Actors` should be created through the *Toolbar* as follows: ![command-actor](/documentation/assets/images/set-actor-command-37e1c78f3139df4350945f5d32f831dc.png) ## 2. Process description[​](#process-description "Direct link to 2. Process description") The *Vacation Approval Process* consist of five simple steps or stages: the request creation, the Manager signing and his approval, the Boss signing and his approval when it might be required based on budget restrictions, the Accounting review and a final step which is the approved request notification once it is paid. Each step can be represented by a single `Activity`. The actors who are involved in the approval process are: the Requester or Author, the Manager, the Big Boss and the Accountant. The vacation approval request or requisition is just a document that should be agreed. The process scheme that describes the sequence of operations is included below. ![image2](/documentation/assets/images/demo-2-081411ea2c59cd75c1948306301ab1f3.png) ## 3. How to run a process[​](#run-process "Direct link to 3. How to run a process") [YouTube video player](https://www.youtube.com/embed/yf6-JY7r7sQ) A vacation request form is created by using ASP.NET application and integrating Workflow Engine. In the documentation section: [How to integrate](https://workflowengine.io/documentation/how-to-integrate), you can read the detailed procedure to integrate an ASP.NET Core web application. Once clicking on the button: `Create vacation request`, the interface for creating a new requisition is displayed, you should complete the following steps: 1. Choose the current employee (who is set as Author initially). 2. Select the Manager from the drop-down list field. 3. Indicate a preferred denotation in the field 'Name'. 4. Set the 'Sum' that is the required amount. 5. Add an observation in the field 'Comment'. 6. The 'Number' is defined automatically. 7. The 'State' is set also by the system. 8. Then, save the data. ![image3](/documentation/assets/images/demo-3-99f3f4054d3f50b9e54b31d994766ddf.png) After saving the created requisition, will be available the *Document's Transition History* where is indicated the available employees who might execute the `Commands` considering the defined Roles and the responsible Actors. The Commands and the Restrictions based on the created `Actors` are defined in the `Transitions`. It is required for triggering the approval workflow. The first available command is: `StartSigning` which enables to go forward to the Activity `ManagerSigning`. ![command1](/documentation/assets/images/demo-command1-5415dcdf62fcc04e16f5dabcb48ba47b.png) If you click on the button: `Open in Workflow Designer`, you can see the current execution state `VacationRequestCreated` in the created workflow diagram instance. ![command1](/documentation/assets/images/demo-state1-dbfa284677df338eb1eacbe985bc1e19.png) When the first command is fulfilled, the state `ManagerSigning` is set, so it is required to change the current employee and select the actor whose can approve or reject the request and perform this activity. Then, the command `Approve` that can be executed by a Manager is displayed. ![command2](/documentation/assets/images/demo-command2-093410b5dd00a1b49c01a5e0881b00e1.png) Then, the current state `ManagerSigning` will appear in the workflow diagram instance. ![demo-state2](/documentation/assets/images/demo-state2-bbabafbedb3e3a9e9a3f5a1af647b9a4.png) As soon as this command `Approved` is executed, is set the state `AccountingReview`, and then it is needed to choose the actor (select the current employee) as well. The same procedure must be repeated until the end when the final activity is accomplished. The Document's Transition History is updated as soon as the activity is completed. It is possible also to choose a specified State in order to resume or restart the workflow process to this State. ![image6](/documentation/assets/images/demo-6-3b28f1712abd34a0b7da80cb3301dc09.png) When the condition: `@Sum > amount` is verified, the command `Approve` permits the state to pass to `BigBossSigning` instead. Accordingly, the current execution state in the workflow diagram instance will be available through `Open in Workflow Designer`. ![image4](/documentation/assets/images/demo-4-26a5b821867cb197e755e6431912dd31.png) Regarding the `Inbox` and `Outbox` pages - In the *Inbox* are listed the pending requests to be handled. On the other hand, in the *Outbox* are listed the requests that have been already performed. * Below, a pending request currently in *Inbox*: ![image-inbox](/documentation/assets/images/demo-inbox-9f40f54df8eea4a8cd34522b4d212e21.png) * Here, the request already fulfilled in *Outbox*: ![image-outbox](/documentation/assets/images/demo-outbox-817b6670b5df5e7e50f933562bd1cc8b.png) The `Users` or `Employees` and their `Roles` are created by implementing a Database model in [Entity Framework](https://learn.microsoft.com/en-us/ef/) which is an Object-Relational Mapping (ORM) that provides developers with an automated mechanism to access and store data in the database. Nevertheless, this kind of data might be available also in a client application or can be retrieved through an authentication server. ![demo-roles](/documentation/assets/images/demo-roles-28e77b5e1f1e9ae699e46d5dbbcfb28b.png) In case of modifying the current scheme, these conditions are met: * An activity can be added to the scheme. * The vacation request can be reopened. * An update to the new scheme is completed when the list of commands is validated. Detailed information regarding this procedure can be read in the section [Process scheme update](https://workflowengine.io/documentation/execution/scheme-update). * When the process is opened in the designer, you will see that the new scheme is used. On the other hand, a new request may be created also as indicated below: ![request-2](/documentation/assets/images/request-2-1527c297f71bc6d564d3cb609571a260.png) Afterward, open the scheme by clicking on: `Open in Workflow Designer` to see the current execution state. ![scheme-change](/documentation/assets/images/scheme-change-71fec1fc1ef062cfcb71d9463f5b2ecb.png) tip In our Github repository our [Samples](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Samples) are available. Detailed information related the Assignment's creation can be read in this documentation [section](https://workflowengine.io/documentation/plugins/assignmentplugin). Assignments This is an alternative method to interact with the process by changing their statuses and other options. The assignments can be handled from both the designer and the document, according the process state. --- # Designer customization This section describes the functions of the Workflow Engine, which allow to change the appearance and behavior of the designer and make it more user-friendly. We will cover the following topics: * [customization](https://workflowengine.io/documentation/designer-customization/appearance-customization) of the designer user interface, includes toolbars, modals, forms, activities and transitions appearance. * [activation](https://workflowengine.io/documentation/designer-customization/autocomplete-provider) of autocomplete to fill in the parameter transferred into Actions, Conditions and Rules. * [assignment](https://workflowengine.io/documentation/designer-customization/parameter-appearance) of edit forms for the parameter transferred into Actions, Conditions and Rules. N.B. When calling Action, Condition or Rule, string parameter is transferred into the called method. You can transfer JSON serialized objects in this parameter. Using autocomplete and form format assignment for parameter editing, you can help users, who are creating process schemes, correctly fill in this parameter. * [languages](https://workflowengine.io/documentation/designer-customization/languages) customization or addition. It enables the Designer UI to be available and translated into several languages. --- # Designer UI Customization availability These features are available from version 5.0 onwards, with some limitations from version 4.0. The architecture of the [designer](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Designer) is highly flexible, allowing for extensive modification of the user interface, including toolbars, forms, modal windows, elements displayed on the canvas, validation, process updating, activity saving, etc. In this section of the documentation, you'll get acquainted with the tools and features of customization. ## Designer Architecture[​](#designer-architecture "Direct link to Designer Architecture") The designer consists of three main components: * **WorkflowDesigner** - a JavaScript object responsible for displaying the canvas, creating and managing elements on the canvas, including HTML and SVG templates, as well as interacting with the WorkflowEngine API. * **HTML Templates** - written using [Vue.js](https://vuejs.org/), containing a Vue application initialization method and used for displaying and handling user interface elements on the canvas: toolbars, modal windows, and forms - all these are templates. * **SVG Templates** - used for displaying the workflow graph on the canvas: activities, transitions, comments, decisions, etc. Angular & React When using the WorkflowDesigner package for React or Angular, WorkflowDesigner is encapsulated within a component that controls the lifecycle of this object. HTML and SVG templates are still used to display elements inside the designer. So, to summarize, the process of displaying the designer looks like this: in your client application, you instantiate WorkflowDesigner directly or use a ready-made [React](https://www.npmjs.com/package/@optimajet/workflow-designer-react) or [Angular](https://www.npmjs.com/package/@optimajet/workflow-designer-angular) component. Through the WorkflowEngine API, WorkflowDesigner loads the workflow schema, data, plugins, activities, rules, etc. Next, for each user interface element, a Vue application is created and initialized by calling the `{template}_Init` function loaded from HTML template, followed by calling the `onUpdate` method with the contextual data for this element. A logical graph is also formed for the schema, and for each graph element, the SVG template rendering function is called, which interpolates the file using the data of that element. As a result, you see the schema and UI elements on the canvas. ## Customizing HTML Templates[​](#customizing-html-templates "Direct link to Customizing HTML Templates") All HTML templates of the designer are located in the [templates](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Designer/templates) folder. This includes toolbars, modal windows, forms, pop-ups, and more. The templates consist of a `{template-name}_Init` function, which initializes the Vue application, its data, and methods; as well as HTML code based on the [Element UI](https://github.com/ElemeFE/element) library for rendering the DOM of the user interface. When a user opens the designer, the WorkflowDesigner object creates a Vue application for each interface element and loads content from the HTML template, for example, `toolbar.html`, then executes the initialization method, in the case of `toolbar.html`, the method name will be `toolbar_Init`. After that, the `me.VueConfig.methods.onUpdate` method will be called with specific data for this element. You can customize any designer templates as you see fit, their appearance, and operation logic. For example, you can add additional validation when saving activities, remove some toolbar functions, or add new activity forms when using [custom activities](https://workflowengine.io/documentation/designer-customization/custom-activity). Changes take effect instantly as templates do not require compilation, but templates are cached in WorkflowDesigner, so remember to clear the browser cache, for example in Chrome, press *F12*, right-click on the reload button, and select *Empty cache and hard reload*. info You can use [designer property](https://workflowengine.io/documentation/main-terms/designer#designer-properties) `cacheLoadedFiles` to disable template caching. toolbar.html ```
``` ## Customizing SVG Templates[​](#customizing-svg-templates "Direct link to Customizing SVG Templates") SVG templates are stored in the [templates/elements](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Designer/templates/elements) folder and are used to display graph elements on the designer canvas. Each SVG template is a file with the extension `.svg`, containing a vector image of the element, styles, text, etc. During rendering, Workflow creates an SVG element on the canvas for each graph element. You can also customize SVG templates as you see fit and even add new types of elements when using [custom activities](https://workflowengine.io/documentation/designer-customization/custom-activity). In this case, you can use interpolation with `{/* Some code */}` to write executable JavaScript code to calculate properties based on the passed data of the graph element. activity.svg ``` {Name} {State} {IsInitialLabel} ``` ### Templates list[​](#templates-list "Direct link to Templates list") * [activity.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/activity.html)[Activity](https://workflowengine.io/documentation/scheme/activities) form. * [activitygroup.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/activitygroup.html)Inline activity form in running process view mode. * [activityinline.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/activityinline.html)[Inline activity](https://workflowengine.io/documentation/scheme/activities) form. * [activitytoolbar.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/activitytoolbar.html)Toolbar with activity forms. * [actors.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/actors.html)Actors form. * [assignments.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/assignments.html)Form to work with the [Assigment plugin](https://workflowengine.io/documentation/plugins/assignmentplugin). * [codeactions.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/codeactions.html)[CodeActions](https://workflowengine.io/documentation/scheme/actions#creation) form. * [codeform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/codeform.html)[Code editing form](https://workflowengine.io/documentation/faq/workflow-engine/how-to-call-a-code-of-my-assembly-from-codeaction) form. * [commandParameters.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/commandParameters.html)Command parameters form. * [commands.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/commands.html)[Commands](https://workflowengine.io/documentation/scheme/commands) form. * [comment.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/comment.html)Comment form. * [commenttoolbar.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/commenttoolbar.html)Comment toolbar. * [confirmDialog.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/confirmDialog.html)Standard form used in confirmation dialog. * customformsCustom forms for plugins. * assignmentpluginCustom forms for [Assigment plugin](https://workflowengine.io/documentation/plugins/assignmentplugin). * [assignmentchangeform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//customforms/assignmentplugin/assignmentchangeform.html)*AssignmentChange* activity from. * [assignmentcreateform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//customforms/assignmentplugin/assignmentcreateform.html)*AssignmentCreate* activity from. * [assignmentshavestatus.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//customforms/assignmentplugin/assignmentshavestatus.html)*AssignmentHaveStatus* activity from. * basicspluginCustom forms for [Basic plugin](https://workflowengine.io/documentation/plugins/basicplugin). * [createprocessform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//customforms/basicsplugin/createprocessform.html)*CreateProcess* activity from. * [httprequestform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//customforms/basicsplugin/httprequestform.html)*HTTPRequest* activity from. * [sendemailform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//customforms/basicsplugin/sendemailform.html)*SendEmail* activity from. * loopspluginCustom forms for [Loops plugin](https://workflowengine.io/documentation/plugins/loopsplugin). * [startloopforeachform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//customforms/loopsplugin/startloopforeachform.html)*StartLoopForeach* activity from. * [startloopforform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//customforms/loopsplugin/startloopforform.html)*StartLoopFor* activity from. * [decision.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/decision.html)Activity form - decision. * [decisiontable.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/decisiontable.html)Activity form - decision table. * [dropdownValues.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/dropdownValues.html)It allows the user to choose one value from a list. * [elements.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/elements.html)Form with a list of available Activities and Inline schemes. * elementsSVG templates folder. * [activity.svg](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//elements/activity.svg)*Activity* SVG template. * [comment.svg](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//elements/comment.svg)*Comment* SVG template. * [decision.svg](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//elements/decision.svg)*Decision* SVG template. * [decisiontable.svg](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//elements/decisiontable.svg)*DecisionTable* SVG template. * [transition.svg](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//elements/transition.svg)*Transition* SVG template. * [expressionform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/expressionform.html)Editing form for [Expressions](https://workflowengine.io/documentation/scheme/conditions#expressions). * [jsonform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/jsonform.html)Form to display user's form that is defined by the user or json editor. * [legend.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/legend.html)Form with legend. * [library.json](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/library.json)JSON file with process template descriptions. * libraryProcess templates, see descriptions in [library.json](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/library.json). * [AutoTransitions.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/AutoTransitions.xml) * [DirectAndReverseTransitionsWithApprovalAndDeny.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/DirectAndReverseTransitionsWithApprovalAndDeny.xml) * [DirectTransitionsWithApproval.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/DirectTransitionsWithApproval.xml) * [DirectoryCompress.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/DirectoryCompress.xml) * [ExpressionConditionOtherwise.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/ExpressionConditionOtherwise.xml) * [ExpressionOtherwise.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/ExpressionOtherwise.xml) * [FileWriteRead.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/FileWriteRead.xml) * [HTTPRequest.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/HTTPRequest.xml) * [LoopForDateTime.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/LoopForDateTime.xml) * [LoopForInt.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/LoopForInt.xml) * [LoopForeach.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/LoopForeach.xml) * [LoopForeachFromParameter.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/LoopForeachFromParameter.xml) * [ParallelApprovalWithoutBranches.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/ParallelApprovalWithoutBranches.xml) * [ParallelProcessesWithWaiting.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/ParallelProcessesWithWaiting.xml) * [ParallelProcessesWithoutWaiting.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/ParallelProcessesWithoutWaiting.xml) * [ParameterExpressionConditionOtherwise.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/ParameterExpressionConditionOtherwise.xml) * [ParameterExpressionOtherwise.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/ParameterExpressionOtherwise.xml) * [ParametersAndExpressions.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/ParametersAndExpressions.xml) * [Timer.xml](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates//library/Timer.xml) * [localization.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/localization.html)Element [Localization](https://workflowengine.io/documentation/scheme/localization) form. * [logs.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/logs.html)Form with [logs](https://workflowengine.io/documentation/scheme/process-logs#process-instance-log). * [parameters.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/parameters.html)[Parameters](https://workflowengine.io/documentation/scheme/parameters) form. * [parametersform.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/parametersform.html)[Editing parameters](https://workflowengine.io/documentation/designer-customization/parameter-appearance#form) form for CodeActions. * [processinfo.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/processinfo.html)Process status display form. * [timers.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/timers.html)[Timers](https://workflowengine.io/documentation/scheme/timers) form. * [toolbar.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/toolbar.html)[Main toolbar](https://workflowengine.io/documentation/scheme#editing). * [toolbarinfo.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/toolbarinfo.html)Bottom toolbar. * [toolbarside.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/toolbarside.html)Side toolbar. * [transition.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/transition.html)[Transitions](https://workflowengine.io/documentation/scheme/transitions) form. * [transitiontoolbar.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/transitiontoolbar.html)Toolbar form displayed at the transition. ## Form customization for Workflow Engine v4.2 and previous versions[​](#form_obsolete "Direct link to Form customization for Workflow Engine v4.2 and previous versions") remember This section provides information for older and unsupported versions of the Workflow Engine. When [creating designer javascript object](https://workflowengine.io/documentation/main-terms/designer) you can fill in the `forms` property in the object which will be transferred into the `WorkflowDesigner` constructor as configuration settings. This is how it looks in code: ``` var wfdesigner = new WorkflowDesigner({ // ... forms: { activity: function (params) { }, transition: function (params) { }, actors: function (params) { }, commands: function (params) { }, timers: function (params) { }, codeactions: function (params) { }, parameters: function (params) { }, localization: function (params) { }, legend: function (params) { }, processinfo: function (params) { } }, // ... }); ``` As you can see in this code, to customize any window you need to write the following *customization function*: ``` function (params) { // ... } ``` and assign it to the appropriate edit window (for example, `activity`). ``` var wfdesigner = new WorkflowDesigner({ // ... forms: { activity: function (params) { // do something }, }, // ... }); ``` *Customization function* accepts the `params` object with the following properties: * `params.data` - data displayed in the form, this property is the object. * `params.elements` - description of the input fields which will be displayed in the form, this property is the objects array: ``` [ { name: "Name", field: "Name", type: "input" }, { name: "State", field: "State", type: "input" } ] ``` * `name` - name displayed in the form. * `field` - name of the `params.data` property, used for binding data to the form. * `type` - control type which will be displayed in the form (for example, "input", "textarea", "checkbox", etc.). * `params.readonly` - if returns true, the form is non-editable. * `params.saveFunc` - basic function to save forms. * `params.title` - form headline. This is the basic algorithm of form customization using *Customization function*: * change the `params` object property. * create a new form object transferring the `params` into object constructor: ``` var form = new WorkflowDesignerForm(params); ``` * create a save form function; it's a typical function, but you can write custom logic in it ``` var saveFunc = function (data) { form.ClearTempField(data); // write any custom code here form.parameters.saveFunc(Object.assign(params.data, data)); return true; }; ``` * call `showModal` function to show modal window. ``` form.showModal(saveFunc); ``` The result code will look like this: ``` var wfdesigner = new WorkflowDesigner({ // ... forms: { activity: function (params) { // change params object var form = new WorkflowDesignerForm(params); var saveFunc = function (data) { form.ClearTempField(data); // write any custom code here form.parameters.saveFunc(Object.assign(params.data, data)); return true; }; form.showModal(saveFunc); }, }, // ... }); ``` note A customization example for these versions can be read [here](https://workflowengine.io/blog/workflow-designer-customization/). ### Activity customization and transition rendering on the Designer canvas[​](#rendering "Direct link to Activity customization and transition rendering on the Designer canvas") When [creating designer javascript object](https://workflowengine.io/documentation/main-terms/designer) you can fill in the `drawElements` property in the object which will be transferred into the `WorkflowDesigner` constructor as configuration settings. This is how it looks in code: ``` var wfdesigner = new WorkflowDesigner({ // ... drawElements: { activity: function (element) { }, transitionActivePoint: function (element, x, y) { }, }, // ... }); ``` * *Customization function* `activity` is responsible for rendering the activity box. The `element` object in the input is the `WorkflowDesignerActivityControl` object instance. Use `element.item` to access the object scheme description. The scheme element itself must be rendered into the `element.control` property. If this function returns false, a standard element will be rendered. * *Customization function* `transitionActivePoint` is responsible for rendering the transition central point. The `element` object in the input is the `WorkflowDesignerTransitionControl` object instance. Use `element.item` to access the object scheme description. The function should return the `Konva.Group` object as a result, which will be displayed on the canvas. If this function returns false, a standard element will be rendered. More information related to rendering customization can be read in [Konva.js documentation](https://konvajs.org/) and the following [example](https://workflowengine.io/blog/workflow-designer-customization/) is also available. --- # Autocomplete setting in the designer using IDesignerAutocompleteProvider Sometimes it is very useful to give a small tip to the user, who is editing the scheme, to prompt him which value to specify for the parameter transferred into Action, Condition or Rule. Very often you've got a *CheckRole* Rule, which checks if a user executing the command has a role with the name transferred into the parameter (*Value* column in the designer). This is how it looks in the designer: ![CheckRole rule](/documentation/assets/images/autocomplete-wfe-1-e39400d6c1cf3e66e181822ce675c78a.png) It is only logical that the system must show a list of roles a user can choose from. To realize that function, you need to do the following: * write a class implementing the `IDesignerAutocompleteProvider` interface. Like this one, for example: ``` public class AutoCompleteProvider : IDesignerAutocompleteProvider { public List GetAutocompleteSuggestions(SuggestionCategory category, string name, string schemeCode) { if (category == SuggestionCategory.RuleParameter && name == "CheckRole") { return new List(){"BigBoss", "Accountant"}; } return null; } } ``` You can see from this code that you need to implement one method only - `GetAutocompleteSuggestions`. Input parameters for this method are: * `category` - enumeration, which defines a scheme element the autocomplete list is called for. Possible values: `RuleParameter`, `ActionParameter` or `ConditionParameter`. * `name` - Action, Condition or Rule name, the autocomplete list is displayed for. In this case autocomplete will work only if you have chosen the *CheckRole* Rule. * `schemeCode` - the scheme code. * specify your Autocomplete provider when configuring `WorkflowRuntime` ``` runtime.WithDesignerAutocompleteProvider(new AutoCompleteProvider()); ``` After that a drop-down list of tips will be available to users. It will look like that: ![Suggestions](/documentation/assets/images/autocomplete-wfe-2-df0f3ad8e3ce7c690242a1012372b025.png) --- # Custom Action parameter control When creating [CodeActions](https://workflowengine.io/documentation/scheme/actions) parameters, you can use a custom Type, and display it on the parameters form using a custom control. To create a custom parameter type, do the following: 1. Go to the parameters of your action. ![Create code action](/documentation/assets/images/create-code-action-e5a91fd8b3b6b2d0d23bb601c49d5a71.png) 2. Create a new parameter and enter the name of your type. Save all. ![Create custom type](/documentation/assets/images/create-custom-type-d3b2986e7f5937a98c7d3bdd19013e3c.png) 3. Add an implementation of this type to the template *jsonform.html* ([How to use custom form](https://workflowengine.io/documentation/designer-customization/custom-activity#custom-form)). ``` ``` 4. Open an activity properties to use your action. ![Action in activity](/documentation/assets/images/action-in-activity-ec864ad47c28520c6a1c9ff23ca14fa2.png) 5. Use your custom control. ![Action params](/documentation/assets/images/action-params-4fa4eb1792078570a5ca4911ffb3e65a.png) --- # Custom Activity Custom [Activity](https://workflowengine.io/documentation/scheme/activities) - the activity with custom edit form and custom visual display on canvas. ### Creating custom activity[​](#creating-custom-activity "Direct link to Creating custom activity") For creating a custom Activity, a class inherited from `ActivityBase` should be created. In the Parameters field of ActivityBase, two parameters, `Name` and `State` are stored; those are the name of the Activity and its state. You can add variables of your own in the constructor. The variables are similar to the parameters in [IDesignerParameterFormatProvider](https://workflowengine.io/documentation/designer-customization/parameter-appearance#provider). ``` Parameters.Add(new CodeActionParameterDefinition() { Name = "Comment", Type = ParameterType.TextArea } ); ``` On entering this Activity, the Workflow Engine calls the `ExecutionAsync` method (`PreExecutionAsync` for [PreExecution mode](https://workflowengine.io/documentation/execution/pre-execution)). Example of a class: ``` using OptimaJet.Workflow.Core; using OptimaJet.Workflow.Core.Model; using OptimaJet.Workflow.Core.Runtime; public class MyCustomActivity: ActivityBase { public MyCustomActivity() : base() { Type = "MyCustomActivity"; Title = "My Custom Activity"; Description = "The action is mine"; // the file name with your form template, without extension Template = "MyFormTemplate"; // the file name with your svg template, without extension SVGTemplate = "MySVGTemplate"; Parameters.Add(new CodeActionParameterDefinition() { Name = "MyField", Type = ParameterType.TextArea } ); } public override async Task ExecutionAsync(WorkflowRuntime runtime, ProcessInstance processInstance, Dictionary parameters, CancellationToken token) { Console.WriteLine("MyCustomActivity:"); foreach(var item in parameters) Console.WriteLine($"{item.Key} - {item.Value}"); Console.WriteLine("--------------"); } public override async Task PreExecutionAsync(WorkflowRuntime runtime, ProcessInstance processInstance, Dictionary parameters, CancellationToken token) { } } ``` ### Settings[​](#settings "Direct link to Settings") You can modify the default settings by overriding them. These settings affect the behavior of the activity in the designer. Below is a list of settings you can use: * AllowCycleTransition: By default, allows the creation of cyclic transitions. If disabled, you won't be able to create a transition from an activity back to itself. * IsOutgoingTransitionsReadonly: If set to `true`, the outgoing transitions of this activity are read-only and can only be edited through the activity form. Default is `false`. * BaseTemplate: Represents the base template which is used to edit the activity. * Activity: Ordinary Activity template or template copied from it. * JsonForm: JSON form template or template copied from it. This form will render `Parameters` as form. * SaveToAnnotationsPolicy: Defines the policy for saving parameters in the designer form. * SaveAllExceptOrdinary: Saves all parameters in annotations except those listed in `OrdinaryActivityParameters`. * DontSave: Parameters will not be saved in annotations. * SaveSpecified: Only the parameters listed in `AnnotationActivityParameters` will be saved in annotations. * OrdinaryActivityParameters: A list of settings that will be saved in the activity properties instead of annotations. * AnnotationActivityParameters: A list of parameters that will be saved in annotations if the `SaveSpecified` policy is enabled in `SaveToAnnotationsPolicy`. ### Custom form[​](#custom-form "Direct link to Custom form") tip If you don't want to create a custom form for an activity, just don't populate the `Template` and `SVGTemplate` fields in your custom activity. Just comment out the fields: ``` // the file name with your form template, without extension //Template = "MyFormTemplate"; // the file name with your svg template, without extension //SVGTemplate = "MySVGTemplate"; ``` To change the Activity form, you should create a new file with the .html extension in the templates folder, and specify its name in the `Template` field. *As an example, you can copy [activity.html](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/activity.html) from the Designer*. Don't forget to rename the initialization function (see the explanation below). An activity template is a [Vue.js](https://v2.vuejs.org/) application. Each template has two parts: 1. HTML. 2. JavaScript. info Each template must contain an initialization function named `templateName_Init`, for example, for a template `activity` it will be `activity_Init`, for a template `decision` it will be `decision_Init`. This function will be called after the template is loaded. The initialization function takes one parameter `me`, which is an instance of the activity window. **Do not use the `-` character in the template name, as well as other characters that cannot be used in the JavaScript function name. Instead, use camelCase for the template name.** You should also add `me.VueConfig.methods.onUpdate` method that accepts one `item` parameter - activity data. In this method, you can initialize the form data. ```

Your form name

``` ### Custom canvas element[​](#custom-canvas-element "Direct link to Custom canvas element") To change the Activity image in the canvas, you should create a new file with the .svg extension in the `templates/elements` folder, and specify its name in the SVGTemplate field. *As an example, you can copy [activity.svg](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/templates/elements/activity.svg?short_path=9314adc) from the Designer*. ### Registration[​](#registration "Direct link to Registration") For registration, the `WithCustomActivities` method of the WorkflowRuntime should be executed: ``` _runtime.WithCustomActivities(new List() {new MyCustomActivity()}); ``` Or you can register only one custom activity: ``` _runtime.WithCustomActivity(new MyCustomActivity()); ``` All custom Activities will be displayed in the **Elements** panel: ![1](/documentation/assets/images/custom-activity-1-d700892b20f2208ae29d89bbc4f0a5c7.png) To add your custom activity to the scheme, proceed as follows: 1. Open the Elements panel. 2. Find the activity in the list of elements or use Search. 3. Drag the element to the canvas. ### Existing templates[​](#existing-templates "Direct link to Existing templates") You can find an existing templates for activities in our [GitHub repository](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Designer/templates) as well as [custom canvas elements](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Designer/templates/elements). --- # Languages There are several elements that might be customized in the visual scheme designer which represents an outstanding advantage and a relevant feature by considering a wide groups of users or client's system requirements. In this article is described a brief procedure to add a new language to the Workflow Engine Designer and enabling it through the Designer UI. ![Added language](/documentation/assets/images/wfe-designer-icon-lang-d02eda6baa96aa14cd4aa403e6804f78.png) ## Adding a custom language to Workflow Engine Designer[​](#adding-custom-language "Direct link to Adding a custom language to Workflow Engine Designer") First, the [WorkflowEngine.NET](https://github.com/optimajet/WorkflowEngine.NET) project repository should be reviewed. The language files that are available for translating purposes can be found through the project root directory in the following location: ``` └─ WorkflowEngine.NET └─ Designer └─ Localization ``` In the `Localization` directory, the file [en\_default.json](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/Localization/en_default.json) can be used as template. It can be translated to the language of your preference. The translation can be completed by using Google Translate or a similar tool and any available open-source text and source code editor. Once the JSON file has been translated, the modified file should be saved in this directory and named according the standard language codes as follows: ``` .json ``` For instance, a translated file `el.json` to implement Greek language: ``` { "LangInfo": { "code": "el", "dialect": "HE", "name": "Greek", "description": "Greek native", // "bidi": "rtl" // this is an optional parameter for right-to-left locales }, "DeleteConfirm": "Είστε βέβαιοι ότι θέλετε να διαγράψετε επιλεγμένα στοιχεία;", "DeleteConfirmCurrent": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτό το στοιχείο;", "EscapeConfirm": "Υπάρχουν μη αποθηκευμένες αλλαγές. Θέλετε να αποθηκεύσετε το σχήμα;", "FieldIsRequired": "Το πεδίο είναι υποχρεωτικό!", ... ``` Next, the custom language might be added by implementing the JavaScript object [WorkflowDesigner](https://workflowengine.io/documentation/main-terms/designer#frontend). In this case the translated json file and JavaScript library for the specific language must be imported and called it through the array `customLocalization: []` from the JavaScript object. Several languages may be included. An example is indicated below: ``` import el from './langs/el.json'; // set language for ElementUI components (https://element.eleme.cn/#/en-US/component/i18n) import elElementUILocalization from '../element-ui/lib/locale/lang/el.js'; el.elementUILocalization = elElementUILocalization; wfdesigner = new WorkflowDesigner({ name: 'simpledesigner', apiurl: '/Designer/API', renderTo: 'wfdesigner', templatefolder: '/templates/', graphwidth: graphwidth, graphheight: graphheight, language: 'en', customLocalization: [el] }); // JavaScript object WorkflowDesigner example ``` ### Selecting the preferred language[​](#selecting-language "Direct link to Selecting the preferred language") Once the modifications has been completed as is described in previous section, the Designer project can be run. After launching the project, the new language will be available in Workflow Engine Designer to be selected in the drop-down list 'Menu' in the section 'Language'. Then, proceed as follows to enable the added language: 1. Open the panel 'Menu'. 2. Choose the option 'Language'. 3. Find the preferred language and click on it. ![Added language](/documentation/assets/images/wfe-designer-languages-977e941944e5dbb1b687f8aebfef8a2d.png) --- # Parameter edit form for Action, Condition or Rule ## General information[​](#general "Direct link to General information") Parameter which is transferred into Action, Condition or Rule can be a complex JSON object, that is why it might be useful to help user fill in this object. In the Workflow Engine you can specify a form which will be displayed in the designer when editing this parameter. There are two ways to describe this form: * In case of Actions, created in the designer (Code Actions), you can describe the parameter form in the designer (i.e. in the scheme), or write a class implementing the `IDesignerParameterFormatProvider` interface and connect it to `WorkflowRuntime` on the server. * On the other hand, Actions implemented through the [Action provider](https://workflowengine.io/documentation/scheme/actions#provider) or [Rule provider](https://workflowengine.io/documentation/scheme/rules#provider) you can use `IDesignerParameterFormatProvider` only. ## Parameter edit form description in the designer[​](#form "Direct link to Parameter edit form description in the designer") Parameter edit form window is called from the *Code Actions* in the Designer toolbar. Each described form is bound to the specific Code Action. ![Code action window](/documentation/assets/images/parameter-wfe-1-f3905591d58116960e73322c8c0d1871.png) You can create fields of the following types: * Text - string input field. * Number - any number. * Checkbox - checkbox field; if the field is non-required, the form will show a three state checkbox - checked, unchecked, undefined. If the field is required, the form will show a two state checkbox - checked, unchecked. * Dropdown - field with a dropdown list to choose a value. * Date/Time - date and time in the ISO 8601 format (for example, 2005-08-09T18:31:42). * Json - any valid JSON. You need to specify name for each field. You can mark the field as required, and the edit form will check if this field has been filled. You can also set value by default for each field. ![Edit parameter window](/documentation/assets/images/parameter-wfe-2-d00356403e44e6806b1e7fa75a13a2d2.png) Parameter edit form is opened upon clicking the Edit value in JSON button. If no parameter edit form has been specified for a certain Code Action, the JSON editor window will open. If it has been specified - the edit form will open. ![Edit parameter form](/documentation/assets/images/parameter-wfe-3-71851aa33f90247fb1ffd719a7b09e63.png) You can always switch into the JSON editor from the edit form and edit parameter value in it. ![Edit parameter Json](/documentation/assets/images/parameter-wfe-4-860e90e34f5b3bcae5c6a87ad45815b1.png) ## Single value parameter[​](#single "Direct link to Single value parameter") Sometimes the parameter transferred into Action, Condition or Rule is a single value, not a complex JSON (for example, number, string, true or false). In this case, the parameter input form must consist of a single nameless field to be created. ![Single parameter window](/documentation/assets/images/parameter-wfe-5-8444623a266a039d16655480a354dc31.png) Then, there will be a single nameless parameter in the parameter edit form. Parameter value will be single, not a JSON object. ![Single parameter value](/documentation/assets/images/parameter-wfe-6-f0a00f6b175924c6a7d2eabc52a89429.png) ## Parameter edit form description on the server - IDesignerParameterFormatProvider[​](#provider "Direct link to Parameter edit form description on the server - IDesignerParameterFormatProvider") If you don't want to create parameter forms in the designer or if your Actions, Conditions or Rules are defined on the server, you can write server code that will specify parameter edit forms appearance displayed in the Designer. * Write a class implementing the `IDesignerParameterFormatProvider` interface, for example: ``` public class DesignerParameterFormatProvider : IDesignerParameterFormatProvider { public List GetFormat(CodeActionType type, string name) { if (type == CodeActionType.Action && name == "UpdateTransitionHistory") { return new List() { new CodeActionParameterDefinition { DefaultValue = "Some text", IsRequired = true, Name = "TextParameter", Title = "Text parameter", Type = ParameterType.Text }, new CodeActionParameterDefinition { DefaultValue = "12", IsRequired = false, Name = "NumberParameter", Title = "Number parameter", Type = ParameterType.Number }, new CodeActionParameterDefinition { DefaultValue = "true", IsRequired = true, Name = "BooleanParameter", Title = "Boolean parameter", Type = ParameterType.Checkbox }, new CodeActionParameterDefinition { DefaultValue = "Value1", IsRequired = false, Name = "DropdownParameter", Title = "Dropdown parameter", Type = ParameterType.Dropdown, DropdownValues = new List { new DropdownValue { Name = "Name1", Title = "Title1", Value = "Value1" }, new DropdownValue { Name = "Name2", Title = "Title2", Value = "Value2" } } }, new CodeActionParameterDefinition { DefaultValue = "2018-11-30", IsRequired = true, Name = "DateParameter", Title = "Date parameter", Type = ParameterType.DateTime }, new CodeActionParameterDefinition { DefaultValue = "{property1: 100, property2: \"Some string\"}", IsRequired = false, Name = "JsonParameter", Title = "Json parameter", Type = ParameterType.Json }, }; } return null; } } ``` This provider example thoroughly describes the form which we have created earlier. You need to implement one method only - `GetFormat`, which will have the following parameters in the input: * `type` - enumeration, which defines a scheme element the parameter edit form is called for (possible values - `RuleGet`, `Action` or `Condition`). * `name` - Action, Condition or Rule name parameter edit form is called for. This form will be displayed only if you have chosen the \* UpdateTransitionHistory\* Action. Method must return a list of fields for the form which will be displayed in the Designer for parameter editing. The code above covers all types of form fields. * Specify your Parameter format provider when configuring `WorkflowRuntime` as follows: ``` runtime.WithDesignerParameterFormatProvider(new DesignerParameterFormatProvider()); ``` ## Substitutions[​](#substitutions "Direct link to Substitutions") The parameter value for Action, Action-Condition, Code Action or Rule is always JSON. Even if a form is used to edit this parameter, it is still JSON. In the JSON, the [process parameter](https://workflowengine.io/documentation/scheme/parameters) values can be substituted. The syntax of substitutions is very similar to the [syntax of expressions](https://workflowengine.io/documentation/scheme/conditions#expressions). * `@ParameterName` - the easiest option, the parameter value named 'parameterName' is substituted from the process. String conversion is done with a simple call of `.ToString()`. * `@ObjectParameter.ObjectProperty.StringProperty` - [partial parameters](https://workflowengine.io/documentation/scheme/parameters#naming) can be used in the expression. That is, if you have a complex, multi-level object stored in the parameter, you can substitute a part of this object. * `@ParameterName:formatString` - expression with a format string. A string formatted according to the `formatString` template is substituted using the format syntax for the .NET platform. Please, for more details see [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting#format-string-component). * `@(ParameterName:formatString)` - if different special characters and delimiters are used in the format string, you must additionally enclose the expression in round brackets. * `@ObjectParameter.ObjectProperty.IntProperty:json` - special formatting when a JSON is used as the JSON value of the action parameter. Below some examples are provided to illustrate substitutions. Suppose, an object named 'Document' is saved in the process parameters in the following form: ``` var Document = new { Amount = 42, Name = "Some Document", Number = 142, CreateDate = DateTime.Today // 17-06-2020 }; ``` The JSON parameter value is: ``` { Amount: "@Document.Amount:json", // we need it here Number: "@Document.Number", // we set in here Title: "@Document.Name number @Document.Number year @Document.Date:yyyy month @Document.Date:MM day @Document.Date:dd" } ``` Then, we get the resulting JSON after the substitution. ``` { Amount: 42, Number: "142", Title: "Some Document number 142 year 2020 month 06 day 17" } ``` --- # Dynamic plugin loading In this tutorial, we'll implement dynamic plugin loading. This allows you to customize the Workflow Engine by placing compiled DLLs with plugins into the *plugins* folder. Plugins are classes that implement `IWorkflowPlugin`, enabling the engine to incorporate new Actions, Rules, etc. Learn more about [plugins](https://workflowengine.io/documentation/plugins). GitHub You can find the code from this tutorial in the [GitHub](https://github.com/optimajet/dynamic-plugin-loading) repository. ### Motivation[​](#motivation "Direct link to Motivation") You might be interested in this functionality for a variety of reasons: * You distribute your software with a Workflow Engine to your clients and want to provide them with customization options without needing access to the source code. * You want to separate dependencies used in the project with the Workflow Engine from those used for plugins. * You manage a large number of plugins and want to simplify their modification or installation. ### Environment Requirements[​](#environment-requirements "Direct link to Environment Requirements") * Application with integrated Workflow Engine. * IDE for working with C# code, such as [Visual Studio](https://visualstudio.microsoft.com/). ## Tutorial[​](#tutorial "Direct link to Tutorial") In this tutorial, we'll step through implementing dynamic plugin loading, which requires: 1. Writing a `PluginLoader` class for dynamically loading assemblies. 2. Adding an extension method for loading plugins into the `WorkflowRuntime`. 3. Creating a new project with plugin implementation and exporting its DLL. 4. Testing and ensuring everything works well. ### PluginLoader[​](#pluginloader "Direct link to PluginLoader") This class will inherit from the System class `AssemblyLoadContext`, aiming to correctly load DLLs and resolve their dependencies at the specified path. ``` using System.Reflection; using System.Runtime.Loader; namespace DynamicPluginLoading; public class PluginLoader : AssemblyLoadContext { private readonly AssemblyDependencyResolver _resolver; public PluginLoader(string path) { _resolver = new AssemblyDependencyResolver(path); } protected override Assembly? Load(AssemblyName assemblyName) { var assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName); return assemblyPath != null ? LoadFromAssemblyPath(assemblyPath) : null; } protected override IntPtr LoadUnmanagedDll(string unmanagedDllName) { var libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName); return libraryPath != null ? LoadUnmanagedDllFromPath(libraryPath) : IntPtr.Zero; } } ``` ### Extension Method[​](#extension-method "Direct link to Extension Method") The extension method allows us to add dynamic loading of plugins into the Workflow Engine Runtime initialization pipeline alongside other settings. ``` using System.Reflection; using OptimaJet.Workflow.Core.Runtime; using OptimaJet.Workflow.Plugins; namespace DynamicPluginLoading; public static class Extensions { private const string PluginsFolder = "plugins"; public static WorkflowRuntime WithDynamicPlugins(this WorkflowRuntime runtime, params string[] plugins) { foreach (var plugin in plugins) { var dllPath = Path.Combine(Environment.CurrentDirectory, PluginsFolder, plugin, $"{plugin}.dll"); try { var loader = new PluginLoader(dllPath); var assembly = loader.LoadFromAssemblyName(new AssemblyName(plugin)); var workflowPluginTypes = assembly.GetTypes() .Where(type => typeof(IWorkflowPlugin).IsAssignableFrom(type)); foreach (var workflowPlugin in workflowPluginTypes) { try { var instance = Activator.CreateInstance(workflowPlugin) as IWorkflowPlugin; runtime.WithPlugin(instance); } catch (Exception) { Console.WriteLine("Create instance failed for plugin: " + workflowPlugin.FullName); } } } catch (Exception) { Console.WriteLine($"Plugin {plugin} not found on path '{dllPath}'"); } } return runtime; } } ``` ### Project with Plugin[​](#project-with-plugin "Direct link to Project with Plugin") To test the functionality, we need to create a test project from which we'll import plugins into the Workflow Engine. This requires several steps: 1. Create a new project `MyPlugin`. ``` dotnet new classlib --name MyPlugin dotnet sln add MyPlugin rm MyPlugin\Class1.cs ``` 2. Add a package reference to `WorkflowEngine.NETCore-Core` to implement `IWorkflowPlugin`. ``` dotnet add MyPlugin package WorkflowEngine.NETCore-Core ``` 3. Add a new class `MyPlugin` implementing the `IWorkflowPlugin` interface. ``` using OptimaJet.Workflow.Core.Runtime; using OptimaJet.Workflow.Plugins; namespace MyPlugin; public class MyPlugin : IWorkflowPlugin { public string Name => nameof(MyPlugin); public bool Disabled { get; set; } public Dictionary PluginSettings => new(); public void OnPluginAdd(WorkflowRuntime runtime, List? schemes = null) { // Do nothing } public Task OnRuntimeStartAsync(WorkflowRuntime runtime) { // Do nothing return Task.CompletedTask; } } ``` 4. Build the project and copy its DLL to the `plugins/{plugin_name}/…` folder. ``` dotnet build mkdir -p $(YOUR_PROJECT_NAME)/bin/Debug/net8.0/plugins/MyPlugin cp -r MyPlugin/bin/Debug/net8.0/* $(YOUR_PROJECT_NAME)/bin/Debug/net8.0/plugins/MyPlugin/ ``` ### Testing[​](#testing "Direct link to Testing") Finally, we have the ability to connect dynamic plugin loading to the WorkflowRuntime creation pipeline with the plugin name specified. ``` var runtime = new WorkflowRuntime() .WithPlugin(new BasicPlugin()) .WithDynamicPlugins("MyPlugin") .AsSingleServer(); ``` If you've followed the steps exactly, `MyPlugin` will appear among the plugins in `WorkflowRuntime`, which you can verify by checking `WorkflowRuntime.Plugins`. ``` foreach (var plugin in runtime.Plugins) { Console.WriteLine($"- {plugin.Name}"); } // Output: // - BasicPlugin // - MyPlugin ``` ## Conclusion[​](#conclusion "Direct link to Conclusion") Now you can easily enhance plugin management in your project. --- # Execution This section describes everything that relates to the order of process execution. In general, process execution represents a constant transition of a process from the Idled status to the Running status and vice versa. When a process is Idled, it waits for a command or a timer to trigger it. The process spends most of its lifetime in this status and does not consume resources in your application. The process state is stored in the persistence store. When a process is in the Running state, it successively executes the Actions attributed to an Activity, calculates conditions and changes its state. Besides, a process has two more statuses: Initialized, which is attributed to a process immediately upon creation, and Finalized, which marks its completion. For more details on how a process is executed, how its states and statuses change, and what events are called in it, refer to the respective [section](https://workflowengine.io/documentation/execution/regular-process). Workflow engine supports parallel branches. Each branch is set up by creating a regular specially marked transition, which leads to the appearance of the subprocess. A subprocess is quite similar to a regular process: it may change its state regardless of the parent process and may be interacted with just like an independent process or via a parent process. A regular process and a subprocess are created and finalized in different ways. A subprocess may merge with the parent process and change the state of the latter. For more details on subprocesses, refer to the respective [section](https://workflowengine.io/documentation/execution/subprocesses). During a regular execution, a process changes its state; however, Workflow Engine has a special mode of process execution simulation, where a process does not change its state, but its Activities are executed. This mode is called PreExecution, and it may be successfully used in a number of business cases. For more details, refer to the [Pre-Execution](https://workflowengine.io/documentation/execution/pre-execution) section. When working with workflow systems, the issue of updating the scheme for processes which are in the run will inevitably arise. Workflow Engine supports two major strategies for process scheme update. It means that old processes may be (automatically) updated to a new scheme, or old processes may keep using the old scheme. For more details on how to use both strategies, refer to the respective [section](https://workflowengine.io/documentation/execution/scheme-update). Besides, this [section](https://workflowengine.io/documentation/execution/scheme-generation) describes how you can modify one of the Workflow Engine's components - scheme generator - to implement various cases, for example, how you can add Actions, which should be executed in each Activity, to the main scheme. --- # Builder steps info These functions are available in the Workflow Engine ≥ 4.0 *Build step* is an additional opportunity to change a process scheme when creating it. When Workflow Engine creates a new process, it executes the following actions: * `WorkflowBuilder` searches for a relevant generated (`IsObsolete` = false) scheme in the *WorkflowProcessScheme* table (search is executed using `SchemePersistenceProvider`). * if a relevant scheme has not been found, [scheme generator](https://workflowengine.io/documentation/execution/scheme-generation) is launched, it returns scheme as XML. For example, the out-of-the-box generator returns scheme from the *WorkflowScheme* table. * `WorkflowBuilder` can be configured to convert the generated scheme to another scheme using *Build steps*. Builder executes system *Build steps* only by default, but you can add custom *Build steps*. * converted scheme is saved to the *WorkflowProcessScheme* table. Every *Build step* is a class inherited from the abstract class `OptimaJet.Workflow.Core.Builder.BuildStep` ``` public class CustomBuildStep : BuildStep { public override string Name => "CustomBuildStep"; public override async Task ExecuteAsync(ProcessDefinition processDefinition) { var builder = Builder; var success = true; try { // some manipulations with process definition var modifiedProcessDefinition = processDefinition.Clone(); // add or remove scheme elements here } catch (Exception ex) { return BuildStepResult.Fail(ex.Message); } if (success) { return BuildStepResult.Success(modifiedProcessDefinition); } return BuildStepResult.Fail("Some error message"); } } ``` From this code you can see that *Build step* must have a `Name`. Also `Execute` method must be implemented, it executes scheme conversion. Implementing this method you can add and delete any elements in the scheme. You have unrestricted access to the `Builder` property in this method. There are two scenarios: * your *Build step* has completed successfully, and you return a new process scheme. By the way, this scheme object may be different from the scheme object in the input `processDefinition` parameter of the `Execute` method. ``` return BuildStepResult.Success(processDefinition); ``` * your *Build step* resulted with an error which will be thrown as exception or displayed in the designer. ``` return BuildStepResult.Fail("Some error message"); ``` You can notify `WorkflowBuilder` that it must execute additional *Build steps* when configuring `WorkflowBuilder` and `WorkflowRuntime`. ``` var builder = new WorkflowBuilder(provider, new XmlWorkflowParser(), provider) .WithDefaultCache(); // these build steps will be executed before system build steps builder.AddBuildStep(0, BuildStepPosition.BeforeSystemSteps, new CustomBuildStep1()); builder.AddBuildStep(1, BuildStepPosition.BeforeSystemSteps, new CustomBuildStep2()); builder.AddBuildStep(2, BuildStepPosition.BeforeSystemSteps, new CustomBuildStep3()); // these build steps will be executed after system build steps builder.AddBuildStep(0, BuildStepPosition.AfterSystemSteps, new CustomBuildStep1()); builder.AddBuildStep(1, BuildStepPosition.AfterSystemSteps, new CustomBuildStep2()); builder.AddBuildStep(2, BuildStepPosition.AfterSystemSteps, new CustomBuildStep3()); runtime = new WorkflowRuntime() .WithBuilder(builder) ... .Start(); ``` You can see from this code that you can specify whether your *Build steps* will be executed before or after system steps. To do that, use the `AddBuildStep` method second parameter which can be `BeforeSystemSteps` or `AfterSystemSteps` value. The `AddBuildStep` method first parameter specifies the steps order for the steps executed before or after system steps. Theoretically you can add your *Build step* into system steps, but we do not advise to do that. Thus, you can build custom step chain which will convert the process scheme generated by the generator. ## System Build steps[​](#system "Direct link to System Build steps") At the moment Workflow Engine has got only one system *Build step* - `InlineSchemesStep`. This step is responsible for the [scheme inlining](https://workflowengine.io/documentation/execution/scheme-inlining), i.e. for embedding schemes into each other in order to re-use typical schemes. ## Build steps and Designer[​](#designer "Direct link to Build steps and Designer") When saving a scheme, the designer executes Build steps to check them, but instead of the resulted scheme it saves the scheme which was drawn in the designer. This check is necessary to ensure that it is possible to create a process based on the saved scheme. Such check is especially important to avoid cycle references during scheme inlining. --- # Pre-execution Workflow Engine has a special mode that simulates process execution. It is called Pre-Execution. The process simulation is required to track the future path of process execution and leave some artifacts in your system. For example, a real user sent a document for approval, and they may want to know what the approval stages are and who will approve the document. This is exactly what the [demo](https://demo.workflowengine.io/) showcases. Before a user sends a vacation request for approval, the Document's Transition History table is filled in the document. It contains the information about what the stages are and who will approve the document. In the abovementioned example, the Pre-execution mode is called every time document status is changed to Idled. The Pre-execution mode is enabled with one of the following methods: ``` await WorkflowInit.Runtime.PreExecuteAsync(processId, "StartActivityName"); await WorkflowInit.Runtime.PreExecuteFromCurrentActivityAsync(processId); await WorkflowInit.Runtime.PreExecuteFromInitialActivityAsync(processId); ``` All three methods differ only by the Activity which Workflow Engine starts the execution simulation with. Let’s see how emulation occurs: * The physical state of a process does not change during simulation. In other words, the values of CurrentState, CurrentActivity and process parameters won’t change. Simulation does not affect the process. * Simulation starts with the Activity that is specified when the `PreExecute` method is called. * Only transitions with Classifier=Direct are taken into account during simulation. * During simulation, the conditions are calculated (if there are any), and only one transition with the Auto or Command trigger is selected from the Activity. Further, the next Activity is executed and a new transition is selected. Simulation ends when: * There are no transitions that can be executed. In other words, there are no transitions with Classifier=Direct, Trigger=Auto or Command, for which the conditions specified in Condition are true. * There is more than one transition that can be executed. During simulation, a process is executed almost in the same way as it would be executed for real, aside from the following points: * When an Activity is executed, Actions are taken not from the Implementation section, but from the PreExecution Implementation section. * In case Workflow Engine finds a transition triggered by a command, it calculates the Restrictions of this transition and fills the `processInstance.IdentityIds` process parameter. In case the transition is a command one, the `processInstance.CommandName` process parameter is also filled. * The calculation of conditions does not differ from the one during regular process execution. However, during simulation, the Result on pre-execution property of the Conditions with the Action type is taken into account. In case this property’s value is set to True or False, the Action won’t be executed, and the specified value will be used. * Besides, during simulation, the `processInstance.IsPreExecution` process parameter is set to true. One of the ways to start simulation during the change of a process state is to launch it from the `OnProcessStatusChangedAsync` event handler: ``` runtime.OnProcessStatusChangedAsync += async (sender, args, token) => { if (args.NewStatus == ProcessStatus.Idled) { await runtime.PreExecuteFromCurrentActivityAsync(args.ProcessId); } }; ``` --- # Lifecycle of a regular process This section describes the order of execution of a process, beginning with the initialization of a process and ending up with it reaching one of the final states (which may not necessarily happen). This section addresses only basic processes; the lifecycle of a [subprocess](https://workflowengine.io/documentation/execution/subprocesses) is a bit different. ![Regular Process](/documentation/assets/images/scheme1-d2442064915561cb7ec7e23a6f1e2f34.png) In general, a process lifecycle comprises initialization and occasional execution of effective work triggered by commands, timers or forced setting of a certain state. The process statuses include the codes which are resumed below: | Value | Name | Description | | ----- | ----------- | ---------------------------------------------------------------------------------------------------------------- | | 0 | Initialized | Status of processes which have been created just now. | | 1 | Running | Status of processes which are being executed at the current moment. | | 2 | Idled | Status of processes which are not being executed at the current moment and are awaiting an external interaction. | | 3 | Finalized | Status of processes which were finalized. | | 4 | Terminated | Status of processes which were terminated with an error. | | 5 | Error | Status of processes which had an error but were not terminated. | ## Initialization[​](#initialization "Direct link to Initialization") Process initialization starts immediately after the `WorkflowInit.Runtime.CreateInstance()` method is called. The following occurs during initialization in the order specified: * Scheme Id is identified. In case the relevant scheme was not found in the WorkflowProcessScheme table (object), a new scheme is built. For more details, refer to the [Scheme versioning](https://workflowengine.io/documentation/execution/scheme-update) and [Scheme generation](https://workflowengine.io/documentation/execution/scheme-generation) sections. * A new process record is made in the WorkflowProcessInstance table (object). The name of the Initial Activity is attributed to the CurrentActivity. The value of the State from the InitialActivity is attributed to the CurrentState. * In case parameters with Purpose=Persistence were conveyed to the process during initialization, or in case Persistence Parameters with Initial Values were declared in the scheme, they get stored in the WorkflowProcessInstancePersistence table (object). In this case, the parameters conveyed to the `CreateInstance` method will prevail. In other words, in case a "ParameterName" parameter with InitialValue = "Value1" was declared in the scheme, and the "Value2" value was conveyed to the CreateInstance method, the parameter value will be "Value2". * In case there are any transitions from the initial Activity that are triggered by timers with undefined values (0 or -1; refer to the [Timers](https://workflowengine.io/documentation/scheme/timers) section), the `runtime.NeedTimerValue` event will be called to specify these timers' values. * Process status is set to Initialized; the `runtime.ProcessStatusChanged` event is called. The process initialization is completed. In case the Implementation section of the Initial Activity is filled in, or there are any transitions triggered by automatic, then: * Process status is set to Running; the `runtime.ProcessStatusChanged` event is called. * Execution process is started and begins with the Initial Activity. In case there is no implementation and Auto transitions, then: * Process status is set to Idled; the `runtime.ProcessStatusChanged` event is called. * The `runtime.OnProcessActivityChanged` event is called. * In case there are transitions from the Initial Activity that are triggered by timers, the timers are registered and start keeping the time. ## Triggering process execution[​](#triggering "Direct link to Triggering process execution") When executing a command or a timer, the execution process looks like this: * A process instance is fetched from the WorkflowProcessInstance and WorkflowProcessScheme tables. In case the scheme already exists in the scheme cache, it won't be transformed into its object representation. * Process status is set to Running; the `runtime.ProcessStatusChanged` event is called. * All saved process parameters are fetched from WorkflowProcessInstancePersistence. If any parameters were conveyed to the command, these process parameters will be updated with command parameter values. Besides, the name of a command or a timer, `IdentityId` and `ImpersonatedIdentityId` are written to process parameters. * All outgoing transitions triggered by a command or a timer with the corresponding name are identified for the current Activity. * A transition is selected according to the following rules: * In case there is a transition with Condition = Always, it will be executed as a matter of priority. * The conditions of transitions with Condition = Action are checked. In case Action Condition (or multiple Action Conditions) returns true, such a transition will be executed. The order for such a check is not defined. * In case no transition was selected upon checking of the conditions, a transition with Condition = Otherwise will be executed. In case a transition was selected: * an **execution process** is launched with the final Activity of the selected transition. In case no transition was selected: * Process status is set to Idled; the `runtime.ProcessStatusChanged` event is called. ## Process execution launched via SetState (SetActivity)[​](#setstate "Direct link to Process execution launched via SetState (SetActivity)") After a forced setting of a state, an execution process looks like this: * A process instance is fetched from the WorkflowProcessInstance and WorkflowProcessScheme tables. In case the scheme already exists in the scheme cache, it won't be transformed into its object representation. * The initial Activity for the set state is searched for (refer to the [Activity](https://workflowengine.io/documentation/scheme/activities) section). The Activity with State = set state and For set state = true is identified. In case Activity was not found, the `ActivityNotFoundException` exception will be thrown. Depending on whether process execution is required or not (determined by call parameters of the `SetState` method), two options are possible: * If process execution is not required: * Process status is set to Running; the `runtime.ProcessStatusChanged` event is called. * Both the Current Activity and the Current State of the process are changed and saved in the WorkflowProcessInstance table (object). * Process status is set to Idled; the `runtime.ProcessStatusChanged` event is called. * If process execution is required: * Process status is set to Running; the `runtime.ProcessStatusChanged` event is called. * All saved process parameters are fetched from WorkflowProcessInstancePersistence. If any parameters were conveyed in the `SetState` method call, these parameters will be updated with conveyed parameter values. Besides, the name of the command="SetState", `IdentityId` and `ImpersonatedIdentityId` is written to the process parameters. * Execution process starts with the Activity being set. ## Activity execution process[​](#execution "Direct link to Activity execution process") Activity execution process runs in the following order: * The `runtime.OnBeforeActivityExecution` event is called. * The following process parameters are set: the value of the executed Activity is attributed to `ExecutedActivity`, the state of the executed Activity is attributed to `ExecutedActivityState`. In case no state is specified, the value of the current state will be attributed to `CurrentState`. * All Actions specified in the Implementation section are called successively. * Upon successful execution of all Actions, `CurrentActivity` = `ExecutedActivity` and `CurrentState` = `ExecutedActivity` are set. * Process state is updated in the WorkflowProcessInstance table (object); a record of the executed transition (in case there is any) is made in the WorkflowProcessTransitionHistory table (object); the process parameters are saved in the WorkflowProcessInstancePersistence table(object). * The `runtime.OnProcessActivityChanged` event is called. * All outgoing automatic transitions for a new Current Activity are identified. * A transition to be executed is selected based on the following rules: * In case there is a transition with Condition = Always, it will be executed as a matter of priority. * The conditions of transitions with Condition = Action are checked. In case Action Condition (or multiple Action Conditions) returns true, such a transition will be executed. The order for such a check is not defined. * In case no transition was selected upon checking of the conditions, a transition with Condition = Otherwise will be executed. In case a transition was selected: * **Execution process** is launched with the FinalActivity of the selected transition. In case no transition was selected: * Process status is changed to Idled; the `runtime.ProcessStatusChanged` event is called. * In case there are any transitions from the new Current Activity that are triggered by timers, these timers will be registered and start keeping the time. Old timers get removed (refer to the [Timers](https://workflowengine.io/documentation/scheme/timers) section). ## Process finalization[​](#finalization "Direct link to Process finalization") In case a process reaches an Activity with the IsFinal = true attribute, then its status will be changed to Finalized instead of Idled after execution is completed. Besides, if the process is a root one (i.e. not a subprocess), **it is not** automatically deleted from the database. You may, for example, return a process to one of the previous states via `SetState` (`SetActivity`). In case your business logic requires automatic deletion of finalized processes, use the following code: ``` runtime.OnProcessActivityChangedAsync += async (sender, args, token) => { if (args.CurrentActivity.IsFinal) await runtime.DeleteInstanceAsync(args.ProcessId); }; ``` or ``` runtime.OnProcessStatusChangedAsync += async (sender, args, token) => { if (args.NewStatus == ProcessStatus.Finalized) await runtime.DeleteInstanceAsync(args.ProcessId); }; ``` ## Error handling[​](#handling "Direct link to Error handling") In case an unhandled exception occurs during Activity execution, the execution process will be interrupted, while the process itself will remain in the previous Activity, i.e. its state won’t be changed. However, the process will remain fully operational. We recommend you to capture and handle exceptions inside the Actions code or in `OnWorkflowError` event. The event handler enables you to change a process state or send an error notification. ``` runtime.OnWorkflowError += (sender, args) => { Exception exception = args.Exception; ProcessInstance processInstance = args.ProcessInstance; TransitionDefinition executedTransition = args.ExecutedTransition; }; ``` --- # Scheme generation We have already mentioned several times that in case Workflow Engine does not find an active scheme in the WorkflowProcessInstance table (object), scheme generation is started. Let's discuss what a scheme generator is and what opportunities for enhancing Workflow Engine's operation it provides. In fact, the `IWorkflowGenerator` interface is defined by a single method. ``` public interface IWorkflowGenerator where TSchemeMedium : class { Task GenerateAsync(string schemeCode); } ``` The `TSchemeMedium` type specifies the type of scheme storing. By default, this is the `XElement` type; in other words, the scheme is stored in XML. The `GenerateAsync` method accepts one parameter: * `schemeCode` - scheme name (code); Each Persistence provider implements the `IWorkflowGenerator` interface. The following code explains how it functions out-of-the-box: ``` public async Task GenerateAsync(string schemeCode) { return await GetSchemeAsync(schemeCode); } public async Task GetSchemeAsync(string code) { using (SqlConnection connection = new SqlConnection(ConnectionString)) { WorkflowScheme scheme = await WorkflowScheme.SelectByKeyAsync(connection, code); if (scheme == null || string.IsNullOrEmpty(scheme.Scheme)) throw SchemeNotFoundException.Create(code, SchemeLocation.WorkflowProcessScheme); return XElement.Parse(scheme.Scheme); } } ``` By default, the generator simply returns the scheme from the WorkflowScheme table (object). The following use cases may be implemented with the help of a scheme generator: * Adjusting of the basic scheme dynamically. It comprises addition of new Activities, Transitions, Actions, etc. * Generation of a scheme according to the business data. For example, you have an object where all the successive process stages are specified, and a process scheme is generated on their basis. * Template-based generation. * Transformation of a scheme created in custom designer into the Workflow Engine scheme. The following code explains how to add standardized Actions to each Activity of an existing scheme. This example is taken from a real project, though it was tailored a bit. ``` public class Generator : IWorkflowGenerator { public async Task GenerateAsync(string schemeCode) { var processDefinition = await WorkflowInit.Runtime.Builder.GetProcessSchemeForDesignerAsync(schemeCode); if (processDefinition == null) { throw SchemeNotFoundException.Create(schemeCode, SchemeLocation.WorkflowProcessScheme); } foreach (var activity in processDefinition.Activities.Where(a => !string.IsNullOrWhiteSpace(a.State))) { if (activity.Implementation.All(c => c.ActionName != "SetDocumentState")) { activity.AddAction( ActionDefinitionReference.Create("SetDocumentState", "0", string.Empty)); } if (activity.IsForSetState) { if (activity.Implementation.All(c => c.ActionName != "UpdateHistory")) { activity.AddAction( ActionDefinitionReference.Create("UpdateHistory", "99", string.Empty)); } if (activity.PreExecutionImplementation.All(c => c.ActionName != "WriteHistory")) { activity.AddPreExecutionAction( ActionDefinitionReference.Create("WriteHistory", "99", string.Empty)); } } } return XElement.Parse(processDefinition.Serialize()); } } ``` --- # Scheme inlining info These functions are available in the Workflow Engine ≥ 4.0 *Scheme inlining* is embedding one scheme into another. You can check almost any scheme as available for inlining and then embed it into other schemes. First of all such mechanism is necessary to re-use typical scheme segments. Inlining is embedding one scheme into another one. The scheme the process will work with is the result of transferring objects of the scheme which is being inlined (*inlined scheme*) into the scheme, code of which is used to create the process (*target scheme*). *Inlined scheme* is checked as available for inlining in the designer. There's a special button for this in the toolbar. There are two required conditions for the scheme to be inlined: * there must be only one Initial Activity in the scheme. * there must be at least one Final Activity in the scheme. Once you have checked the scheme as available for inlining (can be inlined), you must save it. To embed the *inlined scheme* into the *target scheme* do the following: when editing the *target scheme* add *Inline Activity* by clicking the appropriate button in the `Elements` panel. After *Inline Activity* has been added into the scheme, it works the same way as any typical Activity, which means that transitions can enter and exit it normally. ![Inline Activity](/documentation/assets/images/scheme-inlining-1-66b16d2e9f1a306628767602bc717ac7.png) One of the key differences is that *Inline Activity* has only two properties: * `Name` - inline name. * `Inline scheme` - *inlined scheme* code. ![Inline Activity](/documentation/assets/images/scheme-inlining-2-7977e960f80fe8e72d37e878bf1bc2bc.png) When embedding the *inlined scheme* into the *target scheme*, transitions belonging to the *target scheme* are relinked to the Activities of the *inlined scheme*. Transitions entering the *inlined scheme* (i.e. transitions into the relevant *Inline Activity*) will be completed in the initial activity of the *inlined scheme*. Transitions exiting the *inlined scheme* (transitions out of the relevant *Inline Activity*) will start in the first final activity of the *inlined scheme*, if the `Inlined Final Activity Name` field has not been filled for these transitions. If the `Inlined Final Activity Name` field has been filled, these transitions will start in the appropriate Activity of the *inlined scheme*. ![Inline Transition](/documentation/assets/images/scheme-inlining-3-5cbcfeb9b42ad6c50626ee063db7ec30.png) When the *target scheme* is saved in the designer, the designer checks whether it is possible to combine *target scheme* and *inlined scheme*. If not, error is displayed. Workflow Engine supports multi-level inlining when one scheme is inlined into another, and this other scheme is in its turn inlined into the third scheme. There can be an unlimited number of Inline Activities connected to any *inlined schemes* in the *target scheme*. ## Database (Persistence) Providers and inlining[​](#persistence "Direct link to Database (Persistence) Providers and inlining") Two columns were added into the `WorkflowScheme` table to ensure correct inlining: * `CanBeInlined` - signifies that the scheme can be inline into other schemes. * `InlinedSchemes` - is a list of schemes that are inlined into the current scheme. This field is in JSON array format, for example: `["ThirdSimpleInline", "SecondSimpleInline", "FirstSimpleInline"]`. ## Inlining algorithm[​](#algorithm "Direct link to Inlining algorithm") Inlining is executed via special system [build step](https://workflowengine.io/documentation/execution/build-steps). When embedding the *inlined scheme* into the *target scheme* the following transformations of the *inlined scheme* elements take place: * *Process parameters* - are transferred from the *inlined* into the *target* unchanged; if there is already a similar parameter in the *target* (parameters names are compared), parameter type compatibility check is run. If incompatible, an error will be displayed. * *Commands* - are transferred from the *inlined* into the *target* unchanged; if there is already a similar command in the *target* (commands names are compared), command parameter compatibility check is run. Missing command parameters are added from the *inlined* into the *target*. Common command parameters must be linked to the same process parameter. If incompatible, an error will be displayed. * *Timers* - are transferred from the *inlined* into the *target* unchanged; if there is already a similar timer in the *target* (timer names are compared), compatibility check is run. Timers are deemed compatible if all their properties match. If incompatible, an error will be displayed. * *Localizations* - are transferred from the *inlined* into the *target* unchanged; if there is already a similar localization record in the *target* (localization record ObjectName, Type and Culture are compared), compatibility check is run. Localization records are deemed compatible if all their properties match. If incompatible, an error will be displayed. * *CodeActions* - are transferred from the *inlined* into the *target* and are renamed, the rest of the properties remain unchanged. Links to code actions are replaced with new ones in activities, transitions and actors of the *inlined scheme*. * *Actors* - are transferred from the *inlined* into the *target* and are renamed; if the `Rule` property was linked to the Code action from the *inlined scheme*, the link will be replaced with the new name, the rest of the properties remain unchanged. * *Activities* - are transferred from the *inlined* into the *target* and are renamed. If the record in the `Implementation` or `PreExecutionImplementation` was linked to the Code action from the *inlined scheme*, the link will be replaced with the new name. The `IsInitial` property is always defined false. The `IsFinal` property defined false, if there is at least one transition from this Activity in the result scheme. The rest of the properties remain unchanged, including the `State` property. * *Transitions* - are transferred from the *inlined* into the *target* and are renamed. If there are Action type conditions in the transition, and they were linked to the Code action from the *inlined scheme*, the link will be replaced with the new name. If there are restriction in the transition, and they were linked to Actors from the *inlined scheme*, the link will be replaced with the new name. Transitions are relinked to new Activities created in the previous step. We have deliberately not described the *CodeActions*, *Actors*, *Activities* and *Transitions* renaming algorithm, as new names are too complex (especially in case of multi-level inlining), and the renaming algorithm might change. Instead, we have added several properties into each element, which make it searchable, if you know element original name in the *inlined scheme*. They are described in the next section. ## CodeActions, Actors, Activities and Transitions additional properties[​](#properties "Direct link to CodeActions, Actors, Activities and Transitions additional properties") Use the following attributes to search for inlined elements of the scheme: ### CodeAction element property[​](#codeaction "Direct link to CodeAction element property") Hereby `codeActionDefinition` is an object of the `CodeActionDefinition` type. Get the code actions list: ``` List codeActions = processInstance.ProcessScheme.CodeActions; ``` Additional properties: * `codeActionDefinition.WasInlined` - will return true, if this element was inlined when building the scheme. * `codeActionDefinition.OriginalName` - Code Action name in the *inlined scheme*. * `codeActionDefinition.OriginalSchemeCode` - the *inlined scheme* code, i.e. the code of the scheme the Code Action originally belonged to. ### Actor element properties[​](#actor "Direct link to Actor element properties") Hereby `actorDefinition` is an object of the `ActorDefinition` type. Get the actors list: ``` List actors = processInstance.ProcessScheme.Actors; ``` Additional properties: * `actorDefinition.WasInlined` - will return true, if this element was inlined when building the scheme. * `actorDefinition.OriginalName` - Actor name in the *inlined scheme*. * `actorDefinition.OriginalSchemeCode` - the *inlined scheme* code, i.e. the code of the scheme the Actor originally belonged to. ### Activity element properties[​](#activity "Direct link to Activity element properties") Hereby `activityDefinition` is an object of the `ActivityDefinition` type. Get the activities list: ``` List activities = processInstance.ProcessScheme.Activities; ``` Additional properties: * `activityDefinition.WasInlined` - will return true, if this element was inlined when building the scheme. * `activityDefinition.OriginalName` - Activity name in the *inlined scheme*. * `activityDefinition.OriginalSchemeCode` - the *inlined scheme* code, i.e. the code of the scheme the Activity originally belonged to. * `activityDefinition.FirstTimeInlineName` - name of the Inline Activity instead of which the element was inlined for the first time. * `activityDefinition.LastTimeInlineName` - the name of the Inline Activity instead of which the element was inlined for the last time. This value will be equal to `activityDefinition.FirstTimeInlineName` for one-level inlining. ### Transition element properties[​](#transition "Direct link to Transition element properties") Hereby `transitionDefinition` is an object of the `TransitionDefinition` type. Get the transitions list: ``` List transitions = processInstance.ProcessScheme.Transitions; ``` Additional properties: * `transitionDefinition.WasInlined` - will return true, if this element was inlined when building the scheme. * `transitionDefinition.OriginalName` - Transition name in the *inlined scheme*. * `transitionDefinition.OriginalSchemeCode` - the *inlined scheme* code, i.e. the code of the scheme the Transition originally belonged to. * `transitionDefinition.FirstTimeInlineName` - name of the Inline Transition instead of which the element was inlined for the first time. * `transitionDefinition.LastTimeInlineName` - the name of the Inline Transition instead of which the element was inlined. for the last time. This value will be equal to `transitionDefinition.FirstTimeInlineName` for one-level inlining. ## Scheme versioning and inlining[​](#versioning "Direct link to Scheme versioning and inlining") When the *inlined scheme* is saved in the designer, the `IsObsolete` property will be automatically applied to all linked schemes. This link is defined by the `InlinedSchemes` field in the `WorkflowScheme` table. Use the following code to specify the `IsObsolete` property manually: ``` var runtime = WorkflowInit.Runtime; var codesForObsolete = await runtime.Builder.GetRelatedByInliningSchemeCodesAsync(schemeCode); await runtime.SetSchemeIsObsoleteAsync(schemeCode); if (codesForObsolete != null) { foreach (var code in codesForObsolete) { await runtime.SetSchemeIsObsoleteAsync(code); } } ``` Thus, you can get the linked schemes code list by calling the `runtime.Builder.GetRelatedByInliningSchemeCodesAsync(schemeCode)` method. ## Inlining Parameters Mapping[​](#parameters-mapping "Direct link to Inlining Parameters Mapping") info This feature is available in the Workflow Engine ≥ 16.0.0 In the Inline Activity settings, you can configure parameter mapping between the parent scheme and the inlined scheme. Essentially, this is a set of rules that specify the target parameter name and the expression used to calculate its value. These rules are applied in the first implementation of the inlined scheme’s initial activity for input parameters and in the final implementation for output parameters. For each mapping rule, the expression is first evaluated, and then the result is assigned to the target parameter using the `processInstance.SetParameter` method. The parameter’s purpose is specified only if the target parameter is not defined in either the inlined or parent scheme. Example of configuring parameter mapping using the process definition builder: ``` processDefinitionBuilder .CreateInlineActivity("MyInlineActivity", "MyInliningScheme") .SetInputParameters( new("InlinedStringParameter", "\"Value\""), new("InlinedIntParameter", "@ParentIntParameter + 10") ) .SetOutputParameters( new("ParentStringParameter", "@(InlinedStringParameter).Trim()", ParameterPurpose.Persistence), new("ParentIntParameter", "@InlinedIntParameter"), new("InlinedStringParameter", "null") ); ``` In this example, the following mapping rules are defined for the `MyInliningScheme` scheme: * `InlinedStringParameter` - the parameter value will be the literal `"Value"`. * `InlinedIntParameter` - the parameter value will be the value of the parent scheme’s `ParentIntParameter` increased by 10. * `ParentStringParameter` - the parameter value will be the result of calling the `Trim` method on the value of `InlinedStringParameter`. This value will be saved to the database even if the parameter is not defined in either the parent or inlined scheme. * `ParentIntParameter` - the parameter value will be equal to the value of `InlinedIntParameter`. * `InlinedStringParameter` - the parameter value will be deleted from the process. You can also configure parameter mapping using the designer. To do this, open the Inline Activity settings and add or modify parameters in the `Input Parameters` and `Output Parameters` sections. ![Inline Activity Parameters Mapping](/documentation/assets/images/scheme-inlining-parameters-mapping-950e715f3a1dbe6c66cfaf34851ae9cb.png) --- # Schema versioning ![Scheme Update](/documentation/assets/images/scheme2-248340211fccf9baffdf1e2d6a90bfeb.png) There are two strategies for updating the schemes of running processes: * The old process uses the old scheme; * The old process is updated on a new scheme. Note that new processes always use new schemes in both strategies. Workflow Engine supports both strategies and their combinations. On the other hand, the current or most actual version of a scheme is kept in the database table `WorkflowScheme`. ## Launching scheme update[​](#launching "Direct link to Launching scheme update") Workflow Engine considers a scheme obsolete in case IsObsolete = true is attributed to it in the `WorkflowProcessScheme` table (scheme). This attribute is set by default by scheme designer upon its saving. This behavior may be changed by calling the `DesignerAPI` method in the following way (refer to the [Designer](https://workflowengine.io/documentation/main-terms/designer) section): ``` var res = WorkflowInit.Runtime.DesignerAPI(parameters, filestream, false); ``` You may also set the IsObsolete attribute by calling the following method: ``` await WorkflowInit.Runtime.SetSchemeIsObsoleteAsync("SchemeCode"); ``` In case you use a relational database, you may set the IsObsolete attribute with an SQL script. If is needed to create a process or update the scheme of an existing process, but there are no up-to-date (IsObsolete = false) schemes in the `WorkflowProcessScheme` table (object), a scheme generator will be launched. Scheme generator is an object implementing the `IWorkflowGenerator` interface, which was conveyed to `WorkflowRuntime` during configuration. This object will generate a new up-to-date scheme and put it into the `WorkflowProcessScheme` table (object). Once the new scheme is created, the data related to it is saved and the scheme versioning is completed accordingly by setting the field: `IsObsolete = false` in this database table. ## The old process uses a new scheme[​](#new "Direct link to The old process uses a new scheme") Updating a scheme of the running process to a new scheme may be both manual and automatic. ### Automatic update[​](#automatic "Direct link to Automatic update") Workflow Engine supports automatic update of a scheme to a new one upon receiving of the list of available commands. In other words, if a scheme is obsolete, it will be updated for a certain process immediately after calling `WorkflowInit.Runtime.GetAvailableCommands(processId, identityId);`. Thus, automatic update occurs in a lazy mode when it is necessary. In order to enable automatic updates for schemes of existing processes, the following conditions should be satisfied: * Update should be explicitly allowed during configuration of the `WorkflowRuntime` object: ``` runtime.SwitchAutoUpdateSchemeBeforeGetAvailableCommandsOn() ``` * The Auto scheme update value of the Activity, in which a process scheme will be automatically updated, should equal true. ### Manual update[​](#manual "Direct link to Manual update") Manual scheme update occurs after calling the following `WorkflowRuntime` object's functions: ``` await WorkflowInit.Runtime.UpdateSchemeIfObsoleteAsync(processId); ``` The abovementioned `UpdateSchemeIfObsoleteAsync` method version takes the Activity's Auto scheme update property into account. In case this property of the Current Activity equals false, a scheme won't be updated. In order to ignore this property's value, use the following code: ``` await WorkflowInit.Runtime.UpdateSchemeIfObsoleteAsync(processId, false); ``` After each process scheme update, the `OnSchemaWasChanged` event is called, whose handler allows you to change the current Activity of a process if it is not specified in the new scheme. ``` runtime.OnSchemaWasChangedAsync += async (sender, args, token) => { var pi = await runtime.GetProcessInstanceAndFillProcessParametersAsync(args.ProcessId); if (!pi.ProcessScheme.Activities.Any(a => a.Name == pi.CurrentActivityName)) { var initialActivity = pi.ProcessScheme.InitialActivity; pi.CurrentActivityName = initialActivity.Name; await runtime.PersistenceProvider.UpdatePersistenceStateAsync(pi, TransitionDefinition.Create(initialActivity, initialActivity)); } }; ``` ## The old process uses the old scheme[​](#old "Direct link to The old process uses the old scheme") In case you don’t need to update schemes of running processes, simply do not enable automatic scheme update during the `WorkflowRuntime` initialization. In case you don’t explicitly enable it by calling the `SwitchAutoUpdateSchemeBeforeGetAvailableCommandsOn` configuration method, the schemes of the running processes won’t be updated. --- # Setting process state In this article we will describe the mechanism that is provided by Workflow Engine to change or set the State or Activity inside a process. ## ProcessInstance[​](#processinstance "Direct link to ProcessInstance") There are four different methods that can be used in `ProcessInstance` class. These methods can be used inside the CodeAction code, and they can be called in any Activity. Moreover, these methods are useful to skip from one Activity or State to another in the process without including transitions when it could be required. ![Adding CodeAction](/documentation/assets/images/methods-1-87bc10a4102be790bc094f7ac5a230e1.png) ### SetActivityAfterActionExecution[​](#setactivityafteractionexecution "Direct link to SetActivityAfterActionExecution") First, we have `SetActivityAfterActionExecution` [method](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.ProcessInstance\&anchor=processinstance-setactivityafteractionexecution-string-). Just click on CodeAction button in Designer toolbar for including a new Action that will call the method. ![Adding CodeAction](/documentation/assets/images/methods-2-ffc47fe4170dcc7e8f81700facf50e68.png) When this method is invoked the Actions are fulfilled until the settled action, and after that, the process is set in the Activity that is specified according the parameter `activityName`. The process is going to be executed, and it will start on the Activity that was passed as parameter. ![Adding CodeAction](/documentation/assets/images/methods-3-9eb2c40f9894a7135322948681cc840b.png) ### SetActivityAfterActivityExecution[​](#setactivityafteractivityexecution "Direct link to SetActivityAfterActivityExecution") Second, the [method](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.ProcessInstance\&anchor=processinstance-setactivityafteractivityexecution-string-) `SetActivityAfterActivityExecution`. Analogously, you can add the CodeAction that will call this method. ![Adding CodeAction](/documentation/assets/images/methods-4-283e245045a59c382e7f4e4b33c76e02.png) In this case, the parameter `activityName` is also specified, but once this method is called by the CodeAction, the execution of the rest of Actions will proceed and then, the Activity will be set. The process will continue with the specified Activity. ![Adding CodeAction](/documentation/assets/images/methods-5-0794068efc43a50f49f3b4ac8139a436.png) ### SetStateAfterActionExecution[​](#setstateafteractionexecution "Direct link to SetStateAfterActionExecution") Third, we have `SetStateAfterActionExecution` [method](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.ProcessInstance\&anchor=processinstance-setstateafteractionexecution-string-). It is similar to the first described method `SetActivityAfterActionExecution`, but the parameter `stateName` is specified in this case. note Main difference between methods `SetState` and `SetActivity` is described in [this section](https://workflowengine.io/documentation/scheme/activities#difference#setstate#setactivity). ![Adding CodeAction](/documentation/assets/images/methods-6-4516c25438de13fa01cade4b0553b458.png) When this method is invoked, the preceding Actions and the specified one will be executed. After that, the State will be set. ![Adding CodeAction](/documentation/assets/images/methods-7-e963b47a509f795ddb19bbe460c5e4b1.png) ### SetStateAfterActivityExecution[​](#setstateafteractivityexecution "Direct link to SetStateAfterActivityExecution") Finally, the [method](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Model.ProcessInstance\&anchor=processinstance-setstateafteractivityexecution-string-) `SetStateAfterActivityExecution`. ![Adding CodeAction](/documentation/assets/images/methods-8-7c56f62758fc23001d47fb9a05c6fd59.png) This method also works as the method `SetActivityAfterActivityExecution`, but using the parameter `stateName`. Once all the Actions in the Activity are completed, the State will be set. ![Adding CodeAction](/documentation/assets/images/methods-9-d0db7bf8e906dedb0e4b29b2965be3e8.png) That's it! These are the methods that can be used for changing the process state within the process itself. ## BasicPlugin[​](#basicplugin "Direct link to BasicPlugin") When installing the *BasicPlugin*, `SetActivity` and `SetState` actions are available as well. They also implement the methods described in the previous section, and they can be set in any Activity by adding a new Action. info The procedure for connecting the *BasicPlugin* is described in this [section](https://workflowengine.io/documentation/plugins/basicplugin). ![Plugin Action](/documentation/assets/images/methods-10-b639f7bef2a35a51f43b710efa1267ab.png) ### SetActivity[​](#setactivity "Direct link to SetActivity") First, we have the `SetActivity` action. You can select this Action in the Activity implementation section. When it is used, the `SetActivityAfterActionExecution` or `SetActivityAfterActivityExecution` method is called, depending on the Action settings. You just need to choose *AfterAction* or *AfterActivity* in the 'Edit parameter values' window. Besides, there is a mandatory field for indicating the parameter `activityName`. ![Plugin Action](/documentation/assets/images/methods-11-6eae8eb8de42d11650f1565d7d34faeb.png) ### SetState[​](#setstate "Direct link to SetState") Likewise, `SetState` action can be used. If it is set, the `SetStateAfterActionExecution` or `SetStateAfterActivityExecution` method is called, depending on the Action settings. In this case, you can also select *AfterAction* or *AfterActivity*, but the parameter `stateName` must be indicated. In addition, other processes can be handled when the `processId` is specified. If this field has not filled out, then a transition will take place in the same process. ![Plugin Action](/documentation/assets/images/methods-12-0685bd942117926ab38d4a37fcfe1984.png) --- # Subprocesses/Parallel ## Introduction[​](#introduction "Direct link to Introduction") A subprocess is a process that runs together with a regular process. When creating a transition in the designer, you can specify whether it starts or finishes a subprocess. ![1](/documentation/assets/images/subprocesses-wfe-1-1b4dee32ecc4c8bc5caec4921fd239cb.png) To create a subprocess, one transition starting the subprocess is enough. As soon as the engine reaches this transition, it will be executed and the subprocess will be created. ![2](/documentation/assets/images/subprocesses-wfe-2-c50606a903af4535ff48e2113b44e154.png) Workflow Engine supports subprocesses of any level, i.e. a subprocess can create subprocesses of its own. That is to say, if, executing a subprocess, the engine reaches an activity that should start another subprocess, then it will create a sub-subprocess. ![3](/documentation/assets/images/subprocesses-wfe-3-9f0334f5e660ce460a7cfb5d798169dc.png) The hierarchy of subprocesses is a tree. ![2-04](/documentation/assets/images/2-04-7c42bfaafd03d618d3f0e91558a1cf1b.png) First of all, let us define two terms related to the hierarchy of subprocesses to be used further: * Root Process - is the root of the entire hierarchy; this process behaves exactly the same as a regular process. * Parent Process - is the process that directly creates the current sub-process. For the subprocesses of the first level, the parent process is identical to the root process. The lifecycle of a subprocess slightly differs from the lifecycle of a regular process in finalizing the subprocess and updating its scheme. We'll discuss these issues further in this section. ## Application of Subprocesses[​](#application "Direct link to Application of Subprocesses") Subprocesses can be used in many cases. For example: * logical parallelism, for instance, when the process of document approval is parallel for several users, and then, the parallel branches are merged into the root process. * physical parallelism that allows you to run subprocesses in separate threads. * launching a third-party task without changing the state of the root process. * creating a command available from any of the root process states, after creating a subprocess. Sometimes this solution can dramatically simplify the scheme. * management of the root process state from the subprocesses. ## Starting a Subprocess[​](#starting "Direct link to Starting a Subprocess") As noted earlier, a subprocess is started by a transition with the corresponding specification. First of all, you should pay attention to the additional launch settings in the default mode and in the expert mode: ![4](/documentation/assets/images/subprocesses-wfe-4-298439e05278f125ea2b409345bd76b8.png) ![5](/documentation/assets/images/subprocesses-wfe-5-0f8e7b9ed29b65a5c1114f5e6b9b98de.png) You should also take note that a transition able to start a subprocess has a choice of triggers (commands and timers) and conditions to perform the transit; therefore, you can create a subprocess by command, by timer and by setting the conditions for creation. ![6](/documentation/assets/images/subprocesses-wfe-6-d8e594ec75b0a6f3b3c2f492aa83bbd0.png) Let us consider the settings to start a subprocess: 1. Subprocess startup type - the type of the subprocess startup. The following three options are available: *Same Thread*, *Another Thread* and *Timer Queue*. * *Same Thread* - in this case, the subprocess is launched and executed in the same thread as the parent process. The activities of the subprocess and the parent process are executed sequentially. First, the parent process is performed until the transition in it starts the subprocess; then, all of the subprocess activities are executed until it ends; at last, the remaining activities of the parent process are done. See the following figure.
![7](/documentation/assets/images/subprocesses-wfe-7-cccc59e9b799bad402e1316ab19a2440.png) * *Another Thread* - in this case, the subprocess is launched and executed in another thread available from the thread pool, different from the thread of the parent process. The activities of the subprocess and the parent process are executed in parallel. First, the parent process is performed until the transition in it starts the subprocess; then, the activities of the subprocess and the parent process are executed in parallel until they are ended. See the following figure. ![8](/documentation/assets/images/subprocesses-wfe-8-f2305b14756e8e9ecaffff4744e06bff.png) * *Timer Queue* - in this case, the sequence of launching and executing the subprocess is similar to *Another Thread*, but the subprocess is not executed in a separate thread, instead, a timer with a zero response interval is used. What is this mode for? For example, if you apply the [multi-server mode](https://workflowengine.io/documentation/scalability/multiserver) with your Workflow Engine instances using one processor core (which can happen, when deploying in the cloud), then multiple threads will not give anything but slow down the system. Thus, if you want to get parallel execution, you should choose *Timer Queue*. Also, this mode can be used to transfer execution to other instances of the Workflow Engine (or the Workflow Server). 2. *Subprocess name* - here you can specify the subprocess name or write an expression that computes the subprocess name from the process parameters. If the subprocess name is not specified, then it will be the same as the transition name. What is the subprocess name for? Each child of the parent process must have a unique name; to create a second subprocess with the same name is impossible. ![2-10](/documentation/assets/images/2-10-c429f0add8a712513324134bd96bb35f.png) In practical terms, if your subprocess is created by a command and the subprocess name is fixed, then, after the first execution of the command and the subprocess creation, the command will become unavailable. This way you can create only one subprocess. ![9](/documentation/assets/images/subprocesses-wfe-9-d38ea72b2ccd6904c893947b4f2ccfa5.png) However, if the subprocess name is computed from the expression you write in the *Subprocess Name* field, then, changing the process parameters, you can create as many identical subprocesses as you want. ![10](/documentation/assets/images/subprocesses-wfe-10-db722da741bb4e7d430621406439b6d2.png) In the example above, the process name is defined by the expression `@SomeParameter1 + "_" + @SomeParameter2`, thus, by changing one or both of the process parameters, you can create subprocesses with different names that will look like "Parameter1Value\_Parameter2Value". The expression that defines the subprocess name should return a string. Here you can use the full power of the process parameters. A process parameter reference begins with the '@' character. To substitute parameter values into the expression, use the following syntax: * `@ParameterName` - the easiest option, the value of the parameter named 'parameterName' is substituted from the process. The type is the same as the type the parameter value. * `@ObjectParameter.ObjectProperty.StringProperty` - [partial parameters](https://workflowengine.io/documentation/scheme/parameters#naming) can be used in the expression. That is, if you have a complex, multi-level object stored in the parameter, you can substitute a part of this object. * `@ParameterName:formatString` - the expression with a format string. A string formatted according to the `formatString` template is substituted using the format syntax for the .NET platform. Please, for more details see [here](https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting#format-string-component). * `@(ParameterName:formatString)` - if different special characters and delimiters are used in the format string, you must additionally enclose the expression in round brackets. In the expression, any other elements of lambda expressions in C# can be used, see [here](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions) for more details. For a deeper understanding, let us consider how the Workflow Engine interprets the following expression: ``` @SomeParameter1 + "_" + @SomeParameter2 ``` this expression will turn into an asynchronous lambda that returns a string with the new name of the subprocess: ``` async (processInstance) => (await processInstance.GetParameterAsync ("SomeParameter1")) + "_" + (await processInstance.GetParameterAsync ("SomeParameter2")); ``` In the examples above, we describe subprocesses created by a command, but the subprocess name works just the same for subprocesses created by automatic transitions. 3. *Subprocess Id* - since a subprocess is also a process, it always has a unique identifier `ProcessId`. By default, it is always generated by calling `Guid.NewGuid()`; in the vast majority of cases, there is no need to change this behavior. However, in this field you can write the parameter name as "@SomeParameterContainingGuid" and this value of the parameter will be used as the process ID. It's important to note that the process parameter must either be a Guid or contain a string representing a Guid value. 4. *Parameters copy strategy* - when a subprocess is created, it gets a copy of all of the parent process parameters; that is the default behavior. This behavior can be changed using the *Parameters copy strategy* setting. The following options are available. * *Copy All* - the subprocess receives a copy of all of the parent process parameters. * *Ignore All* - the subprocess does not receive any of the parent process parameters. * *Copy specified* - the subprocess receives only the explicitly specified (comma-separated) parameters of the parent process. * *Ignore specified* - the subprocess receives all but the explicitly specified (comma-separated) parameters of the parent process. 5. *Disable parent process control* - disabling the parent process control over the subprocess existence. When the Workflow Engine creates a subprocess, it evaluates the parent process states that allow this subprocess to exist. If the parent process reaches a state that prohibits the subprocess existence, this subprocess is automatically deleted. That is the default behavior. You can disable this mechanism by setting the *Disable parent process control* checkbox. How are the parent process states that control the subprocess existence evaluated? Here is the algorithm description: The algorithm starts at an activity with the transition launching a subprocess, and inspects any activity that can be reached from this point by transitions with Classifier = Direct or Unspecified. The inspection is terminated on the activity with the transition finalizing this subprocess (you can read about finalizing of subprocesses below). Since this algorithm can seem confusing, we illustrate it with the following examples: ![11](/documentation/assets/images/subprocesses-wfe-11-b49a3b0e61e81fb054cb5abccdd2b8af.png) ![12](/documentation/assets/images/subprocesses-wfe-12-7b85e792dc09ca204717ea9d1e55c20e.png) As already mentioned above, a subprocess can be created by a transition with Auto, Command or Timer triggers; moreover, this transition can be conditional. Let us summarize how everything works: * If the transition starting a subprocess has the Auto trigger, then the subprocess will be created after the parent process reaches the activity with this transition. * If the transition starting a subprocess has the Command trigger, then the subprocess will be created by a command. * If the transition starting a subprocess has the Timer trigger, then the subprocess will be created when this timer expires. * If the transition is unconditional, i.e. Condition = Always, then subprocesses will be created regardless of the number of transitions outgoing from the activity. * If the transition is conditional, i.e. Condition = Conditional or Otherwise, then at most one subprocess will be created, regardless of the number of transitions outgoing from the activity. This way you can create different subprocesses depending on different conditions. See the explanatory figure below: ![13](/documentation/assets/images/subprocesses-wfe-13-2e2eea4a25735a8a90cbaa7e87bd9afe.png) ## Finalizing a Subprocess[​](#finalizing "Direct link to Finalizing a Subprocess") A subprocess can either work independently of its parent process or merge into it. At first, let us consider the two simplest cases, when a subprocess works independently of its parent process. ![14](/documentation/assets/images/subprocesses-wfe-14-c657ea705355691dfe1bdeeec7127659.png) In this case, if the subprocess reaches the state marked as final, it will be deleted automatically. If there is no final state in the scheme, then the subprocess will not be deleted automatically. The subprocess can be also merged into the parent. In this case, the subprocess behavior is determined by the output transition. Here are the available settings. ![15](/documentation/assets/images/subprocesses-wfe-15-4471dda2ffdc16d74cc21ef9676995a7.png) ![16](/documentation/assets/images/subprocesses-wfe-16-60139de52c2d3f74ac9c3cf178578484.png) 1. *Merge subprocess via set state* - sets the merge mode. There are two merge modes. * *Merge with the transition to be computed*. In this mode, the *Merge subprocess via set state* checkbox should be unchecked. What happens in this mode? * The subprocess is finalized, and the Workflow Engine tries to execute any transition with the Auto trigger, which comes from the activity where the transition finalizing the subprocess leads. * If the transition is conditional, the conditions are evaluated. * If the transition is unconditional or the conditions return 'true', the transition is performed in the parent process. * Afterwards, the subprocess is deleted. ![17](/documentation/assets/images/subprocesses-wfe-17-e7d87dbb2e69a79f28c1bd62562eafc1.png) In practical terms, this means that you can manage the merging of subprocesses and the parent process. For example, this can be useful for the parallel document's approval (logical parallelism) or for physical parallelism, if necessary. 90% of the required merge cases are covered by the predefined condition of the [Basic Plugin](https://workflowengine.io/documentation/plugins/basicplugin) called *CheckAllSubprocessesCompleted*. ![18](/documentation/assets/images/subprocesses-wfe-18-763a20ac720c545f9ea56b9a2b820ca5.png) This condition has two options of work: * *AllSubprocesses* - returns 'true' if all of the subprocesses have been finalized. That is, we are waiting for the subprocesses only. * *AllSubprocessesAndParent* - returns 'true' if all of the subprocesses have been finalized and the parent process is in the state with the outgoing transition with this condition. That is, we are waiting for both the subprocesses and the parent process. * *Merge with the state*. In this mode, the *Merge subprocess via set state* checkbox must be checked. Then, the following happens: * The subprocess is finalized and the parent process is forcibly set to the state that the transition finalizing the subprocess has reached. * The subprocess is deleted. In practical terms, this means that you can create a command that will be available from any state of the parent process as long as the subprocess exists. For example, this command can rollback the parent process to its initial state. Moreover, other cases of managing the parent process are also feasible. ![19](/documentation/assets/images/subprocesses-wfe-19-c31e33c88b2131f419ac0ecac1ed845b.png) 2. *Parameters merge strategy* - when the subprocess is merged into the parent process, all of its parameters that are absent (or equal to null) in the parent process will be transferred to this parent process. This default behavior can be changed using this setting. The following options are available: * *Overwrite All Nulls* - only those parameters that are absent (or equal to null) in the parent process will be transferred from the subprocess to the parent process. The parameters are mapped by name. * *Overwrite All* - all of the subprocess parameters will be transferred to the parent process; those parameters that already exist in the parent process will be overwritten. * *Overwrite Specified* - only the explicitly specified (comma-separated) parameters of the subprocess will be transferred to the parent process; those parameters that exist both in the parent process and in the subprocess will be overwritten. * *Dont Overwrite Specified* - all of the subprocess parameters but the explicitly specified (comma-separated) ones will be transferred to the parent process; those parameters that exist both in the parent process and in the subprocess will be overwritten. When merging, the parameters are always mapped by name. ## Limitations of Subprocess Creation[​](#limitation "Direct link to Limitations of Subprocess Creation") You can create subprocesses of any level, but if a subprocess is merged into the parent process, then such a transition must be marked as finalizing the subprocess. In the example that is shown in the figure below, the Workflow Engine will not be able to figure out if the activity belongs to a subprocess or the parent process. ![20](/documentation/assets/images/subprocesses-wfe-20-ade84b16488b0096f893db8fbf6c44c4.png) Therefore, it is very important to correctly label the transitions that start and finalize subprocesses. With the correct marking, even the following case becomes possible: ![21](/documentation/assets/images/subprocesses-wfe-21-7221e56e731ab4957e0fb9b2e00dd18c.png) ## Programmatic Access to Subprocess Tree[​](#tree "Direct link to Programmatic Access to Subprocess Tree") Sometimes, to get access to all the currently available subprocess identifiers, the first thing you need is to reach the subprocess tree. For this purpose, the `runtime.GetProcessInstancesTreeAsync` method is used. The code below performs the console output of the subprocess tree. ``` private static async Task WriteProcessTreeAsync(Guid rootProcessId) { var processTree = await WorkflowInit.Runtime.GetProcessInstancesTreeAsync(rootProcessId); if (processTree != null) { var current = processTree.Root; var level = "->"; await WriteSubprocessesAsync(current, level); } } private static async Task WriteSubprocessesAsync(ProcessInstancesTree current, string level) { foreach (var child in current.Children) { Console.WriteLine("{0}SubProcessId = '{1}'. CurrentState: {2}, CurrentActivity: {3}", level, child.Id, await WorkflowInit.Runtime.GetCurrentStateNameAsync(child.Id), await WorkflowInit.Runtime.GetCurrentActivityNameAsync(child.Id)); await WriteSubprocessesAsync(child, level + "->"); } } ``` The `runtime.GetProcessInstancesTreeAsync` method takes as input either the root process identifier or the `ProcessInstance` object and returns the tree root as an instance of the `ProcessInstancesTree` class. Each instance of this class contains the following members: * `Id` - the process Id. * `Name` - the subprocess name. * `StartingTransitionName` - the name of the transition starting the subprocess. * `Parent` - the parent node of the tree, also an instance of the `ProcessInstancesTree` class. * `Root` - the root node of the tree, also an instance of the `ProcessInstancesTree` class. * `IsRoot` - returns true if the current node is the root node. * `Children` - the list of the child nodes for the current node. Thus, having at your disposal the root object of the `ProcessInstancesTree` class, you can receive any information about the subprocess tree. ## Getting the List of Commands and Executing Commands with Subprocesses[​](#commands "Direct link to Getting the List of Commands and Executing Commands with Subprocesses") As you know, to [get the list of commands and execute it](https://workflowengine.io/documentation/main-terms/basic-operations#execution), the following code is used in the Workflow Engine. ``` IEnumerable availableCommands = await runtime.GetAvailableCommandsAsync(processId, identityId); WorkflowCommand selectedCommand = availableCommands.FirstOrDefault(c => c.CommandName == "CommandName"); CommandExecutionResult result = await runtime.ExecuteCommandAsync(selectedCommand, identityId, identityId); ``` How do you get commands for all of the subprocesses? The good news is that you don't need to get the subprocess tree and pull the commands out of each of the subprocesses. The `runtime.GetAvailableCommandsAsync` method returns commands for the current process (determined by processId) and all of its subprocesses. Thus, for most use cases, it is enough to know only the Root Process Id. ![2-24](/documentation/assets/images/2-24-1b2c18735184904f3859abc72bf539b4.png) The `WorkflowCommand` object has the following properties that help to understand which subprocess the command is related to. ``` WorkflowCommand command = ...; Guid processId = command.ProcessId; bool isSubprocessCommand = command.IsForSubprocess; ``` ## Utility Properties of the ProcessInstance class[​](#properties "Direct link to Utility Properties of the ProcessInstance class") An object of the `ProcessInstance` type contains the properties that return the Ids of the root process and the parent process. ``` ProcessInstance processInstance = ...; Guid processId = processInstance.ProcessId; Guid rootProcessId = processInstance.RootProcessId; Guid? parentProcessId = processInstance.ParentProcessId; bool isSubprocess = processInstance.IsSubprocess; ``` Also, when the subprocess is merged into the main process, then all of the subprocess parameters will be available (but only at the merge time) in the special collection: ``` ProcessInstance processInstance = ...; ParametersCollection mergedParameters = processInstance.MergedSubprocessParameters; ``` ## Utility Methods for Merge with Computing of Transition[​](#methods "Direct link to Utility Methods for Merge with Computing of Transition") In the case, when a subprocess is merged with computing the transition, two utility methods are available. They are similar to those connected via the Basic Plugin that we have considered above. ``` bool completed = await runtime.CheckAllSubprocessesCompletedAsync(processInstance); bool completed = await runtime.CheckAllSubprocessesAndParentProcessCompletedAsync(processInstance); ``` ## Getting the List of Users Able to Execute a Command with Subprocesses[​](#users "Direct link to Getting the List of Users Able to Execute a Command with Subprocesses") The last quite useful function allows to retrieve the list of User IDs, who may execute a command in the root process from a subprocess. It is necessary when a subprocess contains a command (for example, a rejection) that may be executed by those users who may execute this command in the root process. ``` var filter = new TreeSearchFilter(subProcessId) { Include = TreeSearchInclude.OnlyStartPoint, StartFrom = TreeSearchStart.FromRoot }; IEnumerable identityIds = await WorkflowInit.Runtime.GetAllActorsForCommandTransitionsAsync(filter); ``` `TreeSearchFilter` contains the conditions for starting and traversing the subprocess tree to get the list of users who may execute the command. This method is most frequently used when working with the inbox. * The process or subprocess identifier known to us is passed to the `new TreeSearchFilter(subProcessId)` constructor. * `StartFrom` - sets the starting point in the subprocess tree, from which to start traversing the tree and getting the list of users. The possible values are: * `TreeSearchStart.FromRoot` - the traversal starts at the tree root. * `TreeSearchStart.FromMe` - the traversal starts at the current node, that is the process, whose ID is specified in the constructor. * `TreeSearchStart.FromParent` - the traversal starts at the parent node (or process), whose ID is specified in the constructor. * `Include` - specifies the direction of traversing the subprocess tree. The possible values are: * `AllDownIncludeStartPoint` - all of the child nodes of the start node (up to the last level of the tree), the start node included. * `AllUpIncludeStartPoint` - all of the parent nodes of the start node (down to the root), the start node included. * `OnlyStartPoint` - the start node only. * `AllDownExcludeStartPoint` - all of the child nodes of the start node (up to the last level of the tree), the start node excluded. * `AllUpExcludeStartPoint` - all of the parent nodes of the start node (down to the root), the start node excluded. ## Setting Root Process Parameters from a Subprocess[​](#parameters "Direct link to Setting Root Process Parameters from a Subprocess") It is possible to safely set and get the root process parameters from a subprocess, using the following methods of an object of the `ProcessInstance` type. Getting a parameter: ``` ProcessInstance subprocessInstance = ... var parameterValue = subprocessInstance.GetRootPersistedParameter("parameter name"); var parameterValue = await subprocessInstance.GetRootPersistedParameterAsync("parameter name"); ``` Setting a parameter: ``` ProcessInstance subprocessInstance = ... subprocessInstance.SetRootPersistenceParameter("parameter name", value); await subprocessInstance.SetRootPersistenceParameterAsync("parameter name", value); ``` Besides, it is recommended to use these methods to work with the parameters of the root process, since they work correctly with the process locks and ensure safe setting of the root process parameters from a subprocess. The Basic Plugin adds exactly the same actions. ![22](/documentation/assets/images/subprocesses-wfe-22-68bb959729e61ad68f689db6e3b89253.png) ## Update of Subprocess Scheme[​](#update "Direct link to Update of Subprocess Scheme") Updating of subprocess schemes is identical to updating of regular process schemes, including automatic updates, event calls, etc., aside from the several points: * A subprocess scheme cannot be updated directly; it is updated during the update of the root process scheme. * During the subprocess scheme update, the name of the subprocess starting transition is used. If a transition with this name remained in the updated scheme, the scheme is divided into subprocesses, and the subprocess scheme gets updated. If there is no transition with such a name in the updated scheme, the `OnStartingTransitionNotFound` is called. You may choose from the three options available in the transition not found handler: * Decide to remove the subprocess: ``` runtime.OnStartingTransitionNotFound += (sender, args) => { args.DropProcess(); }; ``` * Decide that the subprocess (and its scheme) should start with a transition with another name: ``` runtime.OnStartingTransitionNotFound += (sender, args) => { args.StartWithNewTransition("new transition name"); }; ``` * Ignore this situation, skipping the subprocess scheme update: ``` runtime.OnStartingTransitionNotFound += (sender, args) => { args.Ignore(); }; ``` If you don’t change the subprocess behavior in the handler, the subprocess will be deleted. ## Additional Information[​](#additional "Direct link to Additional Information") In order to learn how to create parallel approval within a single stage, refer to [this section](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-parallel-approval-within-a-single-stage) of the documentation. Have a look at these articles to get acquainted with parallel branching examples: [Parallel branching in Workflow Engine](https://workflowengine.io/blog/parallel-branching-in-workflow-engine/) and [Parallel branches. Continued](https://workflowengine.io/blog/parallel-branches-continued/). --- # FAQ: Workflow Engine * [How to choose? Workflow Engine or Workflow Server?](https://workflowengine.io/documentation/faq/workflow-engine/workflow-server-vs-workflow-engine) * [How to Manage User Roles in WorkflowEngine?](https://workflowengine.io/documentation/faq/workflow-engine/manage-roles) * [How to set document status?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-set-document-status) * [How to create document history for status changes?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-document-history-for-status-changes) * [How to create parallel approval within a single stage?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-parallel-approval-within-a-single-stage) * [How to obtain process lists for inbox/outbox folders?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-obtain-process-lists-for-inbox-outbox-folders) * [How to create 'Wait for condition'?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-wait-for-condition) * [How to invite/get all users who can approve a document?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-invite-get-all-users-who-can-approve-a-document) * [How to call a code of my assembly from CodeAction?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-call-a-code-of-my-assembly-from-codeaction) * [Designer troubleshooting.](https://workflowengine.io/documentation/faq/workflow-engine/designer-troubleshooting) * [My workflow is completed just after I have started it.](https://workflowengine.io/documentation/faq/workflow-engine/my-workflow-is-completed-just-after-i-have-started-it) * [I got an error 'CS...' (C# Compiler) in runtime.](https://workflowengine.io/documentation/faq/workflow-engine/i-got-an-error-cs-in-runtime) * [Is Workflow Engine SaaS compliant? Can it run in the cloud?](https://workflowengine.io/documentation/faq/workflow-engine/is-workflow-engine-saas-compliant) * [Which .NET Framework version is required for Core and Designer?](https://workflowengine.io/documentation/faq/workflow-engine/net-framework-for-core-and-designer) * [Is it possible to integrate HTML5 Workflow Designer into an Angular or React application?](https://workflowengine.io/documentation/faq/workflow-engine/integrate-workflow-engine-into-angular-or-react) * [Is it possible to trigger a child scheme from a parent scheme?](https://workflowengine.io/documentation/faq/workflow-engine/trigger-a-child-scheme-from-a-parent-scheme) * [Is it possible to trigger a child process from a parent process?](https://workflowengine.io/documentation/faq/workflow-engine/trigger-a-child-process-from-a-parent-process) * [Does the Enterprise License allow installing Workflow Engine-enabled application on multiple servers within our company?](https://workflowengine.io/documentation/faq/workflow-engine/installing-workflow-engine-enterprise-on-multiple-servers) * [Can I assign multiple users to approve of a document/process?](https://workflowengine.io/documentation/faq/workflow-engine/assigning-multiple-users-to-approve-of-a-document-or-process) * [Error CS0012: The type 'Object' is defined in an assembly that is not referenced.](https://workflowengine.io/documentation/faq/workflow-engine/unreferenced-assembly-error) * [How to config timer by number of working days? E.g. trigger a reminder mail after 5 working days.](https://workflowengine.io/documentation/faq/workflow-engine/how-to-set-working-days-in-timers) * [How to add a drop-down (select) with a stored value to an Activity form](https://workflowengine.io/documentation/faq/workflow-engine/how-to-dropdown-to-activity-form) * [Does the WorkflowInit class need to be static? How can I use the dependency injection in this case?](https://workflowengine.io/documentation/faq/workflow-engine/is-workflowinit-class-static) * [Is the execution of the process thread-safe or transactional?](https://workflowengine.io/documentation/faq/workflow-engine/is-process-execution-thread-safe) * [How to create a dialog for executing a command with parameters?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-dialog-for-command-execution) * [How to create dynamic timers?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-create-dynamic-timers) * [Is it possible to run a designer in Blazor?](https://workflowengine.io/documentation/faq/workflow-engine/is-it-possible-to-run-workflow-designer-in-blazor) * [How to register a license key?](https://workflowengine.io/documentation/faq/workflow-engine/how-to-register-a-license-key) * [How to satisfy strict CSP in Workflow Designer?](https://workflowengine.io/documentation/faq/workflow-engine/strict-csp-designer) In fact, there may be many more questions that those listed. If you have any difficulty working with Workflow Engine, please, feel free to [contact us](https://workflowengine.io/contacts/). --- # Can I assign multiple users to approve of a process? Yes, you can. In fact, you have two options. 1. All assigned users should approve of a document/process. In this case you need [parallel approval without parallel branches](https://workflowengine.io/documentation/plugins/basicplugin#parallel-approval-without-branches). 2. At least one user should approve of a document/process. In this case there are three options: 1. Create a Rule that describes the roles of these people and attach it to a single Transition. 2. Create separate Rules and unite them in a single Transition with the OR clause. 3. Create several Transitions, one for each Rule. Learn more about [rules and roles](https://workflowengine.io/documentation/scheme/rules). Besides, Workflow engine supports [parallel branching](https://workflowengine.io/blog/parallel-branching-in-workflow-engine/) by using [subprocesses](https://workflowengine.io/documentation/execution/subprocesses). --- # Designer troubleshooting Usually problems with the designer occur when JavaScript libraries are initialized incorrectly. ## For Designer versions that come with Workflow Engine 3.0 and later[​](#later "Direct link to For Designer versions that come with Workflow Engine 3.0 and later") The following libraries are required by Designer: * [JQuery](https://jquery.com/) * [jQuery-autoComplete](https://github.com/Pixabay/jQuery-autoComplete) * [Semantic UI](https://semantic-ui.com/) * [ace.js](https://ace.c9.io/) * [Konva.js](https://konvajs.github.io/) In order to troubleshoot Designer, open the browser console by pressing the F12 button. Copy the error text and [send it to us](mailto:support@optimajet.com). **1. A conflict between Semantic UI and Bootstrap is possible** Styles and JavaScript libraries may conflict with each other. If you are using Designer in a Multipage Application (MPA) and if Bootstrap's JavaScript code is not called from the Designer page, then you should not face any issues. In Single Page Applications (SPA) and on mixed MPA pages you should load JavaScript libraries in the following order: first, you should load the Semantic UI library and then Bootstrap. In this case, there will be no conflicts leading to the inoperability of Designer or Bootstrap. The CSS load order does not matter since the CSS conflict has been eliminated in Designer's CSS. info A universal method for resolving conflicts between CSS Frameworks and the Designer is proposed in [this article](https://workflowengine.io/blog/workflow-designer-integration/) ## For Designer versions that come with Workflow Engine versions earlier than 3.0[​](#earlier "Direct link to For Designer versions that come with Workflow Engine versions earlier than 3.0") The following libraries are required by Designer: * [JQuery](https://jquery.com/) * [JQuery UI](https://jqueryui.com/) * [ace.js](https://ace.c9.io/) * [Konva.js](https://konvajs.github.io/) In order to troubleshoot Designer, open the browser console by pressing the F12 button. Here's a list of typical errors: 1. **Error: "Object does not support property or method 'dialog'"** You haven't initialized JQuery UI or you have included JQuery UI twice. 2. **Error: "Unhandled exception at line 5, column ....... in ...../Scripts/workflowdesigner.min.js"** You are using an incompatible version of jQuery. 3. **Problems with mouse control in Chrome** In some cases AdBlock blocks some mouse events. To resolve the issue you need to disable AdBlock and restart your browser. 4. **Error: "Object doesn't support property or method 'addEventListener'" in InternetExplorer** Follow [these instructions](https://social.technet.microsoft.com/wiki/contents/articles/24276.sharepoint-2013-troubleshooting-object-doesn-t-support-property-or-method-addeventlistener-or-getattribute.aspx). 5. **Error: "0x... - JavaScript runtime error: Unspecified error." from kinetic.js library** This happens when the library can't download images. Check that the path to images configured for the Designer object at `imagefolder: '/images/'` property exists and contains images for Designer. --- # How to call a code of my assembly from CodeAction The code of CodeActions is just an ordinary C# code, any methods can be called or any types used from your assembly. It is required to call the following method from the WorkflowRuntime object to register your own types from the assembly, ``` _runtime.RegisterAssemblyForCodeActions( Assembly.GetAssembly(typeof(some type from your assembly))); ``` Below is described how to make use of: `RegisterAssemblyForCodeActions` for implementing code from the backend. 1. First, a new class should be created: ``` public class MyClass { private int Count { get; set; } public MyClass() { Count = 34; } public void IncreaseCount() { Count++; } } ``` 2. This class can be registered like this: ``` workflowServer.WorkflowRuntime.RegisterAssemblyForCodeActions( Assembly.GetAssembly(typeof(MyClass))); ``` 3. Then, the new class can be implemented in visual designer by creating code actions as is shown below: ![Code Actions 1](/documentation/assets/images/code-action-1-5b7bf060d3474ba41ffbff6b17ac4143.png) 4. After clicking on button 'Compile', the following notification should appear: ![Code Actions 2](/documentation/assets/images/code-action-2-d072013a691bbcb0a40b04c1ba771f85.png) 5. Once the code is executed, in debugging mode might be used breakpoints: ![Add breakpoints](/documentation/assets/images/wfe-myclass-3-ae659e0cbe6419bed972f76b73f09562.png) Afterward, all namespaces from your assembly will be added to new CodeActions. --- # How to create a dialog for executing a command with parameters? When executing a command and creating a process instance, you can send parameters that will then be stored in the process. To achieve this, you can create a specific form in the UI where the user can fill in these parameters. You can learn how to implement similar functionality in this [tutorial](https://workflowengine.io/documentation/process-parameters-sample). --- # How to create document history for status changes * Register two functions in IWorkflowActionProvider: * The first function (WriteTransitionHistory) is for making a list. Use the process Instance.IdentityIds to form a list of potential approvers. * The second function (UpdateTransitionHistory) is for adding current action into the table of history changes. * Add these functions into "Implementation" and "Pre-Execute Implementation" units for each Activity that defines the document status. * Subscribe to ProcessStatusChanged event in WorkflowRuntime. In the command handler, do the following: * Delete empty history fields (if exists). * Call PreExecuteFromCurrentActivity method in WorkflowRuntime. ## Example of method for PreExecute Implementation[​](#example-of-method-for-preexecute-implementation "Direct link to Example of method for PreExecute Implementation") ``` public static void WriteTransitionHistory(ProcessInstance processInstance, string parameter) { if (processInstance.IdentityIds == null) return; var currentstate = WorkflowInit.Runtime.GetLocalizedStateName(processInstance.ProcessId, processInstance.CurrentState); var nextState = WorkflowInit.Runtime.GetLocalizedStateName(processInstance.ProcessId, processInstance.ExecutedActivityState); var command = WorkflowInit.Runtime.GetLocalizedCommandName(processInstance.ProcessId, processInstance.CurrentCommand); using (var context = new DataModelDataContext()) { GetEmployeesString(processInstance.IdentityIds, context); var historyItem = new DocumentTransitionHistory { Id = Guid.NewGuid(), AllowedToEmployeeNames = GetEmployeesString(processInstance.IdentityIds, context), DestinationState = nextState, DocumentId = processInstance.ProcessId, InitialState = currentstate, Command = command }; context.DocumentTransitionHistories.InsertOnSubmit(historyItem); context.SubmitChanges(); } } ``` ## Example of method for Implementation[​](#example-of-method-for-implementation "Direct link to Example of method for Implementation") ``` public static void UpdateTransitionHistory(ProcessInstance processInstance, string parameter) { var currentstate = WorkflowInit.Runtime.GetLocalizedStateName(processInstance.ProcessId, processInstance.CurrentState); var nextState = WorkflowInit.Runtime.GetLocalizedStateName(processInstance.ProcessId, processInstance.ExecutedActivityState); var command = WorkflowInit.Runtime.GetLocalizedCommandName(processInstance.ProcessId, processInstance.CurrentCommand); using (var context = new DataModelDataContext()) { var historyItem = context.DocumentTransitionHistories.FirstOrDefault( h => h.DocumentId == processInstance.ProcessId && !h.TransitionTime.HasValue && h.InitialState == currentstate && h.DestinationState == nextState); if (historyItem == null) { historyItem = new DocumentTransitionHistory { Id = Guid.NewGuid(), AllowedToEmployeeNames = string.Empty, DestinationState = nextState, DocumentId = processInstance.ProcessId, InitialState = currentstate }; context.DocumentTransitionHistories.InsertOnSubmit(historyItem); } historyItem.Command = command; historyItem.TransitionTime = DateTime.Now; if (string.IsNullOrWhiteSpace(processInstance.IdentityId)) historyItem.EmployeeId = null; else historyItem.EmployeeId = new Guid(processInstance.IdentityId); context.SubmitChanges(); } } ``` --- # How to Create Dynamic Timers caution Do not use implicit timers, use expression timers with parameters instead. You can utilize parameters as the timer value. Supported types include `DateTime`, `TimeSpan`, `string`, and `int`: * `DateTime` - the timer will be set to the received date. * `TimeSpan` - the value will be added to the current date. * `string` - it will trigger an interval timer if possible. Otherwise, the method [GetCustomTimerValueAsync](https://workflowengine.io/documentation/api-reference/OptimaJet/Workflow/Core?api=OptimaJet.Workflow.Core.Runtime.WorkflowRuntime\&anchor=workflowruntime-getcustomtimervalueasync) will be called. * `int` - the value will be added to the current date as milliseconds. For more information about timers you can refer [here](https://workflowengine.io/documentation/scheme/timers) and about `GetCustomTimerValueAsync` method [here](https://workflowengine.io/documentation/faq/workflow-engine/how-to-set-working-days-in-timers). ## Example of working with expression timer with parameters.[​](#example-of-working-with-expression-timer-with-parameters "Direct link to Example of working with expression timer with parameters.") [YouTube video player](https://www.youtube.com/embed/w_EHopQHTs4?si=jM6TOuDEBnBYe5Nj) --- # How to create parallel approval within a single stage To create a stage with simultaneous concordance among several users, do the following: 1. In IWorkflowRuleProvider, register a rule to check the user access rights to concordance. The access should be defined by taking into account concordances previously executed at this stage. Executed concordances are recorded in WorkflowProcessTransitionHistory table. 2. Add a record into the "Actors" block with a rule from step 1. 3. Add a cyclical Transition to the current Activity with the following parameters: Trigger Type: Command, Restrictions: Actor from step 2. 4. In IWorkflowActionProvider, register an Action that will check whether or not the stage has been fully concurred. Executed concordances are recorded in WorkflowProcessTransitionHistory table. 5. Add an outgoing Transition from the current Activity with the following parameters: Trigger Type: Auto, Condition Action: Action from step 4. ![Parallel Branches](/documentation/assets/images/parallel-approval-1-19a8450e3afd7135f49153782642f2ce.png) --- # How to create 'Wait for condition' To create a stage with simultaneous concordance among several users, do the following: 1. In IWorkflowActionProvider, register an action to check condition. 2. Add a "WaitTimer" timer. 3. Add a cyclical Transition to the current Activity with the following parameters: Trigger Type: Timer, Trigger Timer: WaitTimer, Condition Type: Otherwise. 4. Add an outgoing Transition to a current Activity with the following parameters: Trigger Type: Auto, Condition Action: Action from step 1, Condition Result on PreExecution: True. ![Wait for Condition](/documentation/assets/images/wait-for-condition-1-b7d7874135be6780ce05f9c5eec64633.png) --- # How to add a drop-down (select) with a stored value to an Activity form In WorkflowEngine, you can fully customize and modify the designer's forms to suit your needs. This article will provide an example of adding dropdown input on a regular activity form. ## Example[​](#example "Direct link to Example") After completing all the steps in this guide, you will get the following result: ![Custom dropdown example](/documentation/assets/images/dropdownOnForm-67a3672df7c4ac0abb813d5d4ca13328.gif) ## Guide[​](#guide "Direct link to Guide") * Open the file Designer/templates/activity.html. * Insert the following element: ``` ``` * Add the following code snippet to the initialization of the component: ``` function activity_Init(me) { me.VueConfig.data = Object.assign(me.VueConfig.data, { // insert after this line FormId: {}, FormIds: ["Form1", "Form2", "Form3"], }) //... } ``` * Insert the code to the function `me.VueConfig.methods.onUpdate`, after initialization of `formdata.Annotations`: ``` me.VueConfig.methods.onUpdate = function (item) { // ... formdata.Annotations = // ... ; // insert after this line var getAnnotationIndex = function (annotationName) { var annotations = formdata.Annotations; var foundIndex = annotations.findIndex(function (annotation) { return annotation.Name === annotationName; }); return (annotations.length > 0) ? (foundIndex >= 0 ? foundIndex : annotations.length) : 0; } var FormIdI = getAnnotationIndex("FormId"); if (!(formdata.Annotations[FormIdI])) { formdata.Annotations[FormIdI] = { Name: "FormId", JsonValue: "" }; } me.VueConfig.data.FormId = formdata.Annotations[FormIdI]; } ``` These are all changes. Pretty simple, right? Now open the action form in the Workflow Builder and see the result. --- # How to invite/get all users who can approve a document For example, you have Transition A -> B and you need to notify users which can approve (or reject) a document in Activity B. You have two options to do that. **1st option:** To notify users who can do something with your document in Activity B. You need to put a notification code to Activity B. Use Code Actions or IWorkflowActionProvider to do that. When you execute a transition from A to B, the implementation of Activity B will be executed. **2nd option:** You can write code that determines and notifies respective users in ProcessStatusChanged or OnProcessActivityChanged (in versions >= 1.5.5) event handlers. **You can take next user's ID in the following way:** ``` var usersIds = await runtime.GetAllActorsForCommandTransitionsAsync(processInstance); ``` If you need to get a list of users who can do something with a parent process from a subprocess, you can use the following code (in versions >= 1.5.5): ``` using OptimaJet.Workflow.Core.Subprocess; var filter = new TreeSearchFilter(processInstance) { StartFrom = TreeSearchStart.FromRoot, Include = TreeSearchInclude.OnlyStartPoint }; var usersIds = await runtime.GetAllActorsForCommandTransitionsAsync(filter); ``` --- # How to obtain process lists for inbox/outbox folders ## For Inbox[​](#for-inbox "Direct link to For Inbox") 1. Create a WorkflowInbox table (Id, ProcessId, IdentityId). 2. Subscribe to ProcessStatusChanged event in WorkflowRuntime and run the following algorithm: 1. Delete all records from the WorkflowInbox table for the current ProcessId. 2. Using GetAllActorsForDirectCommandTransitions, get IDs of all users who can execute commands for the current Activity. 3. Add the necessary records to WorkflowInbox table based on the data from the previous step. 3. To get the list of incoming processes, filter the IdentityId field in the WorkflowInbox table. 4. To calculate the list of incoming documents, clear the WorkflowInbox table and call GetAllActorsForDirectCommandTransitions for every active process. If there are a lot of active processes, this operation may take a long time. ## For Outbox[​](#for-outbox "Direct link to For Outbox") A list of documents agreed upon by the user (or his assistant) can be obtained by filtering WorkflowProcessTransitionHistory table by ExecutorIdentityId and ActorIdentityId fields. --- # How to register a license key info For more information on obtaining a license key, please visit [this page](https://workflowengine.io/documentation/license-key#obtaining-a-license). Once you have received your key, register it using the integration approach that your application uses. ## Workflow Engine Core[​](#workflow-engine-core "Direct link to Workflow Engine Core") If you use Workflow Engine Core directly and create `WorkflowRuntime` manually, register the license key in `WorkflowRuntime`: ``` WorkflowRuntime.RegisterLicense("your license key text"); ``` ## Workflow Engine Web API[​](#workflow-engine-web-api "Direct link to Workflow Engine Web API") If you use [Workflow Engine Web API](https://workflowengine.io/documentation/web-api), add the license key in `Program.cs` when configuring `WorkflowApiCoreOptions`: ``` builder.Services.AddWorkflowApiCore(SetupWorkflowApiCore); void SetupWorkflowApiCore(WorkflowApiCoreOptions options) { options.LicenseKey = "Put your license key here"; } ``` important For Workflow Engine Core, this code must be executed before starting Workflow Engine. The license key string must include the prefix of the company name or the prefix "TRIAL-". --- # How to set document status Subscribe to ProcessStatusChanged event in WorkflowRuntime. The following event is called when the process status is changed. To get the name of the status use: ``` e.ProcessInstance.CurrentState ``` To get the name of the status in your locale use the GetLocalizedStateName method: ``` var nextState = WorkflowInit.Runtime.GetLocalizedStateName(e.ProcessId, e.ProcessInstance.CurrentState); ``` Following this, set the status value in your database. --- # How to config timer by number of working days? E.g. trigger a reminder e-mail after 5 working days info Starting with WFE 11.0 you can use [work calendars](https://workflowengine.io/documentation/scheme/working-calendars) for this task In order to customize the timer, you should create a handler for `GetCustomTimerValueAsync`: ``` runtime = new WorkflowRuntime(); runtime.GetCustomTimerValueAsync += GetCustomTimerValueAsync; ... runtime.Start(); ``` This handler is called when registering the timer with TimerType = Expression, and the Value string that differs from the format: "2d 5h 24m 15s", "2days 5hours", etc. Thus, by implementing the handler, you can send and process any values necessary to implement an algorithm of your own. Example of using the handler: ``` class TimerValue { public int WorkingDays { get; set; } } public static async Task GetCustomTimerValueAsync(string value, string name) { TimerValue timerValue = null; try { timerValue = JsonConvert.DeserializeObject(value); } catch { } if (timerValue != null) { // TODO: Add your algorithm for calculating working days. return runtime.RuntimeDateTimeNow.AddDays(timerValue.WorkingDays); } return null; } ``` In the designer, creation of a new custom timer will look as follows: ![Workflow Timer Value](/documentation/assets/images/wfdesigner-expression-timer-a559e065ec5ffc25e13cb07dcb74fda0.png) --- # I got an error 'CS...' (C# Compiler) in runtime This means that the code in your Code Actions is not valid. You can check whether CodeActions compiled using the Compile button in Edit Code window in the designer. ![Code Editor](/documentation/assets/images/cs-in-runtime-1-618c32abec8e115a1fe5e19c1f849a96.png) To register your own types from the assembly, you need to call the following method from the WorkflowRuntime object. ``` _runtime.RegisterAssemblyForCodeActions(Assembly.GetAssembly(typeof(some type from your assembly))); ``` --- # Can I install Workflow Engine Enterprise on multiple servers? Yes, Workflow Engine Enterprise license does not put any limitations on the number of end-users and servers using the application. --- # Can I integrate Workflow Designer into an Angular or React app? Yes, you can. In fact, Workflow Engine's Designer can be integrated into any HTML page, since it runs on JavaScript. Examples: 1. [WorkflowEngine Designer for Angular Sample](https://github.com/optimajet/workflow-designer-angular-sample) 2. [WorkflowEngine Designer for React Sample](https://github.com/optimajet/workflow-designer-react-sample) 3. [WorkflowEngine Designer for JavaScript Sample](https://github.com/optimajet/workflow-designer-javascript-sample) Say you’ve got the following error: ``` __WEBPACK_IMPORTED_MODULE_3__external_workflowdesigner_min_js__ is not a constructor ``` Probably, that means that you tried to import the Workflow Designer like this: ``` import WorkflowDesigner from './scripts/workflowdesigner.min.js' ``` Please, don’t do that. It doesn't work, because WorkflowDesigner is defined as a global JS object. In order to integrate it into an Angular or React application, do the following: 1. Include workflowdesigner.min.js and other libraries to your HTML page like this: ``` ``` 2. Add a div with the id attribute to an Angular template or a render method in React: ```
``` 3. After it’s rendered, use the WorkflowDesigner global JS object (or window\.WorkflowDesigner) like this: ``` var wfdesigner = new WorkflowDesigner({ name: 'simpledesigner', apiurl: '/Designer/API', renderTo: 'wfdesigner', imagefolder: '/Images/', graphwidth: 1200, graphheight: 600 }); ``` You can find more information on how to initialize WorkflowDesigner's JS object and required libraries [here](https://workflowengine.io/documentation/main-terms/designer). Besides, you can have a look at an example of integrating Workflow Designer into an Angular application on [GitHub](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Other/AngularTest). This sample uses the resources and the server-side of our demo stand available at [workflowengine.io](https://workflowengine.io/). You need to turn on CORS in your browser for the sample to work properly. You can do that via a [Chrome extension](https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf). Use a search engine to look for solutions for other browsers. ![Angular 1](/documentation/assets/images/angular-1-fb085d8326d976438f9b6048181426f6.png) ![Angular 2](/documentation/assets/images/angular-2-b48e560694320f4e551632e750b12852.png) --- # Is it possible to run Workflow Designer in Blazor? Yes, it is possible to integrate Workflow Designer into a Blazor application. For more information on how to do so, please refer to the [article](https://workflowengine.io/documentation/workflow-designer-in-blazor-application). --- # Is the execution of the process thread-safe or transactional? The execution of a process in the Workflow Engine is thread-safe but not transactional. Thread safety is ensured through the process status and locks. When a process changes its status from Idle to Running, it becomes locked for further requests, such as executing commands or triggering timers. For example, if two commands are called concurrently, only the first one will execute, and then the process will be locked, preventing the execution of the second command, which will result in an error. Regarding transactions, when discussing processes in the Workflow Engine, there is no concept of a transaction. This means that you cannot isolate the execution of a series of commands in any way or rollback the results of process execution. --- # Is Workflow Engine SaaS compliant? Can it run in the cloud? Yes, Workflow Engine can be easily integrated into any SaaS application and can run on any cloud environment capable of running .NET or .NET Core. Workflow Engine’s processes are isolated from one another. Hence, you can execute them on different servers, having as many instances of WorkflowRuntime as you need, whereas each WorkflowRuntime instance can use a separate database for Persistence. Besides, Workflow Engine can be scaled by separating processes by scheme code. If you want to run multiple instances of the Workflow Engine connected to the same database, you must configure the WorkflowRuntime as multi-server with the following code. ``` runtime.AsMultiServer(); ``` --- # Does the WorkflowInit class need to be static? How can I use the dependency injection in this case? It is absolutely not necessary. `WorkflowRuntime` does not have to be static, and neither where it is located in `WorkflowInit` class. However, it is highly recommended having `WorkflowRuntime` as Singleton. Most DI Frameworks support Singleton pattern, so you just need to create `WorkflowService` non-static class and register it as Singleton. `WorkflowInit` must be Singleton and also must include `WorkflowRuntime`. --- # How to Manage User Roles in WorkflowEngine? You can use BasicPlugin to manage roles in the Workflow Engine. ## How to manage roles[​](#how-to-manage "Direct link to How to manage roles") ### Predefined actors[​](#predefined-actors "Direct link to Predefined actors") You can add [actors](https://workflowengine.io/documentation/scheme/rules#rule) to use them directly in the restriction. To achieve this goal you need to register the actor name in the basic plugin with the `basicPlugin.WithActors` function. Then subscribe two delegates - `basicPlugin.CheckPredefinedActorAsync` and `basicPlugin.GetPredefinedIdentitiesAsync` to check an actor, and to return all identity (i.e. user) ids which are this actor. ``` private static WorkflowRuntime InitWorkflowRuntime() { ... var basicPlugin = new OptimaJet.Workflow.Core.Plugins.BasicPlugin(); basicPlugin.CheckPredefinedActorAsync = CheckPredefinedActorAsync; basicPlugin.GetPredefinedIdentitiesAsync = GetPredefinedIdentitiesAsync; basicPlugin.WithActors(new List() {"Manager", "Author"}); var runtime = new WorkflowRuntime() ... .WithPlugin(basicPlugin) .Start(); ... return runtime; } async Task CheckPredefinedActorAsync(ProcessInstance processInstance, WorkflowRuntime runtime, string parameter, string identityId) { var myManagerId = ... // Example: checking that the user with the specified Id is a Manager if (parameter == "Manager") { return identityId == myManagerId; } return false; } async Task> GetPredefinedIdentitiesAsync(ProcessInstance processInstance, WorkflowRuntime runtime, string parameter) { var myManagerId1 = ... var myManagerId2 = ... // Example: return id of all users who are Managers if (parameter == "Manager") { return new List() {myManagerId1, myManagerId2}; } return new List(); } ``` ### UsersInRoleAsync delegate and CheckRole rule[​](#delegate "Direct link to UsersInRoleAsync delegate and CheckRole rule") You can create one function which will allow the Workflow Engine to check the role. I.e. it will be an integration point with your security system. To implement this, subscribe for the `basicPlugin.UsersInRoleAsync` delegate: ``` private static WorkflowRuntime InitWorkflowRuntime() { ... var basicPlugin = new OptimaJet.Workflow.Core.Plugins.BasicPlugin(); basicPlugin.UsersInRoleAsync = UsersInRoleAsync; var runtime = new WorkflowRuntime() ... .WithPlugin(basicPlugin) .Start(); ... return runtime; } public static async Task> UsersInRoleAsync(string roleName, Guid? processId = null) { // TODO return all identityIds (userId) for this roleName return new List() {"identity1", "identity2", "identity3"}; } ``` Then, the *CheckRole* method becomes available in the WorkflowDesigner. In the Actors section, add the following roles: ![Actors](/documentation/assets/images/roles-actors-eb951348dde18d0ff23e5c5ef0e6441e.png) * *Name* – the Actor name to be used in the workflow scheme. * *Rule* – set the *CheckRole* from the BasicPlugin. * *Parameter* – the role name to be used when calling `UsersInRoleAsync`, it will be passed in the delegate function as the `roleName` parameter. ## How to use it[​](#how-to-use "Direct link to How to use it") Next, you can limit access to Transitions, using Actors: ![Transition](/documentation/assets/images/roles-transition-e8f42713c3ac69bad8c5f69715649d14.png) When invoking [basic operations](https://workflowengine.io/documentation/main-terms/basic-operations), you specify the *identityId* parameter. This is the identifier of the user, who has initiated the operation (for example, the operation of obtaining a list of available commands). When checking the user’s access to commands, in case the Workflow Engine meets Actor with *CheckRole* Rule in the scheme, the BasicPlugin calls `UsersInRoleAsync`. It checks if the *identityId* parameter is contained in the array returned by `UsersInRoleAsync`. If so, then the command that launches Transition (see picture above) becomes available to the user; and, the user becomes able to execute this command and start the transitional process. caution If your system is heavily loaded, and the performance is critical for you, then we recommend creating a separate IWorkflowRuleProvider with the RuleGet and RuleCheck methods that check user roles. You can read more [here](https://workflowengine.io/documentation/scheme/rules). --- # My workflow is completed just after I have started it Most likely the scheme of the workflow includes [AA transitions](https://workflowengine.io/documentation/scheme/transitions). They are executed automatically and have a priority. Follow [this link](https://workflowengine.io/documentation/scheme) for more information. --- # Which .NET version is required for Core & Designer? Workflow Engine runs on .NET Framework 4.6.2 and higher or Microsoft.NETCoreApp version higher than 1.1. Workflow Engine supports .NET 6 and .NET 8. You can find more information about supported .NET versions in [this part](https://workflowengine.io/documentation/#supported-net-versions) of the documentation. --- # How to satisfy strict CSP in Workflow Designer? If you enforce a strict Content Security Policy (CSP) and disallow inline styles or styles injected via JavaScript, use the strict entrypoint. In this mode the designer styles are loaded from a separate CSS file. ## Solution[​](#solution "Direct link to Solution") 1. Import the designer from `@optimajet/workflow-designer/strict`. 2. Include `@optimajet/workflow-designer/dist/workflowdesigner.min.css` as a separate stylesheet. Examples for each package are below. ## JavaScript/TypeScript (core package)[​](#javascripttypescript-core-package "Direct link to JavaScript/TypeScript (core package)") ``` import WorkflowDesigner from "@optimajet/workflow-designer/strict"; import "@optimajet/workflow-designer/dist/workflowdesigner.min.css"; const designer = new WorkflowDesigner({ apiurl: '', name: 'wfe', language: 'en', renderTo: 'root', graphwidth: window.innerWidth, graphheight: window.innerHeight, customLocalization: [el] }); ``` If you include styles in HTML, use an external stylesheet: ``` ``` ## React[​](#react "Direct link to React") ``` import WorkflowDesigner from '@optimajet/workflow-designer-react/strict'; import '@optimajet/workflow-designer-react/css'; export function DesignerPage() { return ; } ``` ## Angular[​](#angular "Direct link to Angular") ``` import { WorkflowDesignerStrictModule } from "@optimajet/workflow-designer-angular/strict"; @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, WorkflowDesignerStrictModule ], }) ``` Add the CSS to `angular.json`: ``` { "styles": [ "node_modules/@optimajet/workflow-designer-angular/workflowdesigner.min.css" ] } ``` Then use the component in your template: ``` ``` --- # Can I trigger a child process from a parent process? Yes, you can. Use the [API](https://workflowengine.io/documentation/main-terms/basic-operations) to work with processes from the Actions code. The calls that you will make will not be different from any other calls you make outside the process. --- # Can I trigger a child scheme from a parent scheme? Yes, you can, [read more](https://workflowengine.io/documentation/plugins/basicplugin-api#actions). --- # Error CS0012: The type 'Object' is defined in an assembly that is not referenced You're trying to compile your project and get the following error: ``` The type 'Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'netstandard, Version=2.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51 ``` Adding a reference to NETStandard.Library doesn't help. Probably, that means that you're using .NET Framework and .NET Standard in one solution. Your solution is to avoid using them at the same time since Microsoft doesn't support it. For more information, please, refer to [this GitHub issue](https://github.com/dotnet/standard/issues/391). --- # How to choose? Workflow Engine or Workflow Server? There are key elements that should be kept in mind in order to choose the solution that better fits specifications and requirements: * First, the functionality that is needed. It should be considered. If Workflow Engine's functionality is enough, so you might move forward and select this solution. * Which assembly is required - a standalone application or a library? If a quick start-up is a must, the best option is definitely the Workflow Server in a docker container. * If the applications are not written in .NET and there are no .NET developers available, then a better option will be Workflow Server. * In case of building the solution in an existing .NET application, it is better to choose Workflow Engine. * How the client's components or functionalities to the product will be added? If further enhancements will often go through the integration of external services - a better choice would be Workflow Server, otherwise - Workflow Engine. * The degree of customization is also important. If the solution implies a wide customization, Workflow Engine is the best option. If a lot of connections and additional servers are needed to integrate Workflow Server into the solution, as a result of architecture development, then it may be worth reconsidering the approach and embedding Workflow Engine into one of the servers. * In addition, the price. Workflow Engine is cheaper than Workflow Server. * Finally, the following relevant criteria should be evaluated to choose a solution properly: | Criteria | Workflow Engine | Workflow Server | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Set of functionalities | - Workflow Engine (Runtime).
- Workflow Designer.
- Workflow version control.
- Integration with other components.
- Plugins
| - Everything that is supported in the Workflow Engine.
- REST API.
- HTTP API.
- Swagger.
- Administration panel.
- Simple user forms.
- Importing users from LDAP.
- Integration with OpenID.
- Logging to file/console/DB.
- Import/Export configuration.
- Form builder.
- End-user interface.
| | Distribution type | Workflow Engine (Runtime), distributed as nuget packages:
- [.NET Standard 2.0 packages](https://www.nuget.org/packages/WorkflowEngine.NETCore-Core/).
Workflow Designer, distributed as JavaScript library:
- [Plain JavaScript library](https://www.npmjs.com/package/@optimajet/workflow-designer).
- [React library](https://www.npmjs.com/package/@optimajet/workflow-designer-react).
- [Angular library](https://www.npmjs.com/package/@optimajet/workflow-designer-angular).
| It is a standalone .NET 8 application. | | Cross-platform | Nuget packages are available for .NET Standard 2.0 which is compatible with .NET Framework 4.6.2 and above. | It works well on .NET 8. | | Docker container | You can create your own docker container with Workflow Engine. | Yes, [there is](https://hub.docker.com/r/optimajet/workflowserver). | ### Workflow Server[​](#wfs "Direct link to Workflow Server") The latest version of Workflow Server is available in the [Downloads section](https://workflowserver.io/downloads/). [These instructions](https://workflowserver.io/documentation/how-to-launch/) should be followed to run WorkflowServer with the provided database or \[Dockerhttps\://workflowserver.io/documentation/docker) can be used to deploy it automatically. First, a scheme must be created. It can be done either from scratch or by using one of the ready-made schemes on [our test server](https://demo.workflowserver.io/?apanel=workflow). We highly recommend starting with the simplest, [TestScheme](https://demo.workflowserver.io/?apanel=workflow\&aid=TestScheme), which contains three states and two transitions with commands. After creating the scheme, the [Workflow API](https://demo.workflowserver.io/?apanel=workflowapi) can be used from the admin panel to manage the processes. For instance, a process based on the scheme can be created, the list of available commands is enabled, and commands are executed through this interface. Each section provides code examples of how to call methods from an external system using the HTTP API. Access to the API and admin panel can be restricted from the Security tab on the [Settings](https://demo.workflowserver.io/?apanel=settings) section. In addition, an external system with users, roles, etc. might be integrated with the Workflow Server. More detailed information is provided in [this guide](https://workflowserver.io/documentation/faq/manage-roles). The functionalities can be extended by applying the [Code Actions](https://workflowengine.io/documentation/scheme/actions#creation) or rebuilding WorkflowServer using the Visual Studio solution that is delivered with the package. Starting with version 2.6, the user interface constructor and user form creator are available, like in a CMS. In version 2.7, users are authorized to access forms. ### Workflow Engine[​](#wfe "Direct link to Workflow Engine") An instance of [WorkflowRuntime](https://workflowengine.io/documentation/main-terms/runtime) must be created to start working. This object provides the access to the C# API for creating and managing workflow instances. The key functions are: * CreateInstance – create a process (workflow) instance. * GetAvailableCommands – get the list of available commands. * ExecuteCommand – execute a command. * SetState – set a state. * DeleteInstance – delete a process (workflow) instance. Further details can be read [here](https://workflowengine.io/documentation/main-terms/basic-operations). We recommend starting with one of the examples, where *WorkflowRuntime* is already configured and an example of a workflow is given. The current available sample applications for WebForms, ASP.NET MVC and ASP.NET Core are: * [WebForms (.NET Framework) samples](https://workflowengine.io/downloads/net/) * [ASP.NET MVC (.NET Framework) samples](https://workflowengine.io/downloads/net/) * [ASP.NET Core (.NET Core) samples](https://workflowengine.io/downloads/net-core/) In case of Angular, there are examples based on the [standard angular](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Other/AngularTest) and [angular boilerplate](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Other/AbpAngularSample/6.5.0) solutions. Unfortunately, other examples are not provided because they’re rarely used, and creating and supporting them requires considerable effort. Workflow Engine stores basic information regarding processes in: *WorkflowProcessScheme* and *WorkflowProcessInstance* tables in the database. Further information can be read [here](https://workflowengine.io/documentation/main-terms/persistence). Once acquaintance with the product is reached, is possible to start integrating it into a project. Our [step-by-step integration guide](https://workflowengine.io/documentation/how-to-integrate) gives guidance to move forward on the topic. If managing users and roles in the schemes is planned, the engine must be integrated with the security system. [This article](https://workflowengine.io/documentation/faq/workflow-engine/manage-roles) explains in detail how to perform the task using the *Basic Plugin*. WorkflowEngine has extensive design customization options. For example, owned [Actions](https://workflowengine.io/documentation/scheme/actions) can be declared on the backend, and a set of parameters for the call might be specified. Based on this description, WorkflowDesigner automatically shows the [form with the parameters specified](https://workflowengine.io/documentation/designer-customization/parameter-appearance). --- # Forms and Workflow ## Forms Plugin Overview[​](#forms-plugin-overview "Direct link to Forms Plugin Overview") Forms Plugin is a solution that provides a ready-to-use API and UI connecting Workflow Engine with [Form Engine](https://formengine.io/). It lets you render and submit workflow-bound forms, validate and persist data, and execute available process commands. Form Engine Form Engine is a client-side library for creating web forms. It maps JSON to a dynamic HTML form and sends the results to your server. Learn more at [formengine.io](https://formengine.io/). In this section, we’ll outline at a high level what the *Forms Plugin* is. Additionally, in the following sections we’ll cover how to connect it, what API capabilities it provides, and walk through a detailed example of integrating forms into a React application. ![forms\_001](/documentation/assets/images/forms_001-9e989b61e34c93f66fae90deb97b72dc.png) This figure shows a typical application structure with its frontend and backend parts. Sections highlighted in green are delivered as part of the *Workflow Engine Forms Plugin* package; the remaining parts must be implemented on your side. 1. **End-user application.** Your application into which you embed forms associated with workflow processes and available to end users. 2. **Forms Viewer component.** We provide this component to render forms to the end user. Put simply, it displays the form, the set of workflow commands associated with it, and a *Save* button to store the form without advancing it through the workflow. This React component, built on [Optimajet Form Engine](https://formengine.io/), is included in the *Forms Plugin*. 3. **Form Controller.** The *Forms Viewer* component communicates with the server through a dedicated controller. We designed the *Forms Plugin* so you can change the communication format. Don’t worry—the controller is simple, and you can copy it from the example that will be covered later. 4. **Forms Plugin.** The *Forms Plugin* handles all form-related requests. It processes both requests for executing forms and requests for saving form designs. 5. **Workflow Runtime.** 6. **Workflow Database.** All tables required for forms are included in the distribution. 7. **Admin application.** Your application where administrators or power users can create and edit forms in a visual editor. 8. **Forms Manager component.** The visual form editor. This React component, built on [Optimajet Form Engine](https://formengine.io/), is included in the *Forms Plugin*. 9. **Designer Controller.** The *Forms Manager* component saves form designs through this controller. If you’ve already connected Workflow Designer, nothing else is required—everything you need is already implemented. Now, to make this concrete, here’s what it looks like. ## Forms Manager component.[​](#forms-manager-component "Direct link to Forms Manager component.") ![forms\_002](/documentation/assets/images/forms_002-d5433e95b99ebec946d4851c0eb19a24.png) **Key elements:** 1. A list of all forms and their versions. 2. A form you can edit visually. 3. Settings for the selected component. The Visual Form Builder used here has many features; see the [documentation](https://formengine.io/documentation/) for a complete list. ## Forms Viewer component.[​](#forms-viewer-component "Direct link to Forms Viewer component.") ![forms\_003](/documentation/assets/images/forms_003-a1e5cbac553b1820087e8e71d7e46d80.png) **Key elements:** 1. The form itself, displayed to the user. 2. Multiple forms may be available to the user; if more than one form exists, they can choose which form to open. 3. Command buttons. At the moment, only one command *Submit* is available, so only one button is shown. 4. A *Save* button to store the form without advancing it through the workflow. ## Core features of the Forms Plugin[​](#core-features-of-the-forms-plugin "Direct link to Core features of the Forms Plugin") * Provides a visual form editor embeddable in your application with the full capabilities of [Optimajet Form Engine](https://formengine.io/). * Renders forms to the end user. * Supports both client-side and server-side validation, which can be combined seamlessly. * Stores form data in process parameters, meaning you don’t need to create additional tables in your database if you don’t want to. * Is fully compatible with the Workflow Engine [external parameters](https://workflowengine.io/documentation/scheme/parameters#external) feature, so you can store form data in your existing tables—we’ll show how in the example. * Controls access to forms via process commands, letting you manage form access in one place. * Can be used with your own forms engine if you prefer. ## [📄️ How to connect](https://workflowengine.io/documentation/forms-connection) [How to connect forms to Workflow Engine](https://workflowengine.io/documentation/forms-connection) ## [📄️ Forms Plugin features](https://workflowengine.io/documentation/forms-features) [Forms Plugin features description](https://workflowengine.io/documentation/forms-features) ## [📄️ Sample project](https://workflowengine.io/documentation/forms-sample) [Sample project for working with Forms Plugin](https://workflowengine.io/documentation/forms-sample) --- # How to Connect Forms to Workflow Engine In this section, we’ll give a high-level overview of how to connect forms that run alongside workflow processes to your application. Before you begin, we strongly recommend reviewing the [sample project](https://workflowengine.io/documentation/forms-sample) we provide and reading the detailed description of all form settings in the following sections. In short, here’s the sequence of steps: 1. [Connect](https://workflowengine.io/documentation/how-to-integrate) *Workflow Engine* to your application. 2. Connect the *Forms Plugin* to the *Workflow Runtime*. 3. Implement the *Forms Controller*. 4. If you don’t yet have a controller for the designer API, implement one. 5. Add the *Forms Viewer* component to your frontend. 6. Add the *Forms Manager* component to your frontend. ## Backend[​](#backend "Direct link to Backend") ### Connect the Forms Plugin to the Workflow Runtime[​](#connect-the-forms-plugin-to-the-workflow-runtime "Direct link to Connect the Forms Plugin to the Workflow Runtime") ``` var formsPluginSettings = new FormsPluginSettings { FormsManagerUrl = configuration.Workflow.FormsManagerUrl, }; var formsPlugin = new FormsPlugin(formsPluginSettings); WorkflowRuntime runtime = new WorkflowRuntime() .WithPlugin(formsPlugin) ... ``` You only need to specify one required parameter, *FormsManagerUrl*. It has a single purpose: to let the process-scheme designer open the form designer at the right moment. This will be explained in detail in later sections and doesn’t significantly affect the plugin’s behavior. ### Implement the Forms Controller[​](#implement-the-forms-controller "Direct link to Implement the Forms Controller") You’ll need to implement the Forms Controller methods yourself because frontend-to-backend communication differs across projects. The required methods are straightforward, and we plan to provide a standard implementation soon in the [Workflow Engine Web API](https://workflowengine.io/documentation/web-api) project. You can try them out in the sample project discussed in the [following sections](https://workflowengine.io/documentation/forms-sample). You need one method that retrieves a form by its name and version number. It’s straightforward—it just returns the form without tying it to any process. ``` [HttpGet] [Route("form")] public async Task> GetForm([FromQuery] string formName, [FromQuery] int? formVersion) { GetFormResult formResponse = await _runtime.GetFormsRuntimeApi().GetFormAsync(new GetFormParameters { FormKey = new FormKey { FormName = formName, FormVersion = formVersion } }); return formResponse.Match( ok => Ok(ok.Form), error => Problem(error.Message, statusCode: 500) ); } ``` Next, you need a method that returns the list of forms available to a specific user within a specific process. Note that you don’t have to read the user ID from the request; most likely you’ll get it from the application context. This code is copied from the sample project, where, for simplicity, there is no authorization or authentication, but there is a user ID that can be switched. ``` [HttpGet] [Route("get")] public async Task> GetForms([FromQuery] Guid processId, [FromQuery] string user) { FormsRuntimeApi formsPluginRuntimeApi = _runtime.GetFormsRuntimeApi(); GetExecutableFormsResult executableFormsResponse = await formsPluginRuntimeApi .GetExecutableFormsAsync(new GetExecutableFormsParameters { ProcessId = processId, IdentityId = user, ConditionCheck = true }); return executableFormsResponse.Match( ok => Ok(ok.Forms.Select(f => f with { FormData = f.FormData.ToCamelCase() }).ToArray()), error => Problem(error.Message, statusCode: 500) ); } ``` Now we need a method that saves the form without advancing it through the workflow. ``` public record SaveFormApiRequest(FormKey FormKey, Guid ProcessId, string User, Dictionary Data); [HttpPost] [Route("save")] public async Task> SaveForm([FromBody] SaveFormApiRequest request) { Dictionary pascalCaseData = request.Data.ToPascalCase(); SaveFormResult response = await _runtime.GetFormsRuntimeApi().SaveFormAsync(new() { FormKey = request.FormKey, ProcessId = request.ProcessId, IdentityId = request.User, FormData = pascalCaseData }); return response.Match( ok => Ok(ok.Data.ToCamelCase()), validationErrors => BadRequest(validationErrors.Errors.ToCamelCase()), error => Problem(error.Message, statusCode: 500) ); } ``` Finally, you need a method that executes a command together with the form. ``` public record ExecuteFormApiRequest(FormKey FormKey, string CommandName, Guid ProcessId, string User, Dictionary Data); public record ExecuteFormApiResponse(bool WasExecuted); [HttpPost] [Route("execute")] public async Task> ExecuteForm([FromBody] ExecuteFormApiRequest request) { Dictionary pascalCaseData = request.Data.ToPascalCase(); ExecuteFormResult response = await _runtime.GetFormsRuntimeApi() .ExecuteFormAsync( new ExecuteFormParameters { FormKey = request.FormKey, CommandName = request.CommandName, ProcessId = request.ProcessId, IdentityId = request.User, FormData = pascalCaseData }); return response.Match( ok => Ok(new ExecuteFormApiResponse(ok.WasExecuted)), validationErrors => BadRequest(validationErrors.Errors.ToCamelCase()), error => Problem(error.Message, statusCode: 500) ); } ``` In general, those are all the essential methods for form interaction in the user-facing application. Keep in mind that we continually convert data from *PascalCase* to *camelCase*. You don’t have to do this, but remember that **the Form Engine binds data by component keys on the form**; if everything coming from your server is in *PascalCase*, you’ll need to name the keys with an initial capital letter. ``` Dictionary pascalCaseData = request.Data.ToPascalCase(); ... return response.Match( ok => Ok(ok.Data.ToCamelCase()), validationErrors => BadRequest(validationErrors.Errors.ToCamelCase()), error => Problem(error.Message, statusCode: 500) ); ``` ### Implement the Designer Controller[​](#implement-the-designer-controller "Direct link to Implement the Designer Controller") If you already know how to connect the process-scheme designer, then you’ve already implemented this controller, and all the methods for saving forms are in place. If not, read [this section](https://workflowengine.io/documentation/main-terms/designer#backend). ## Frontend[​](#frontend "Direct link to Frontend") ### Connect the Forms Viewer component[​](#connect-the-forms-viewer-component "Direct link to Connect the Forms Viewer component") On the client side, in the end-user application, you need to implement a component that renders the form to the user. Important points: * *showError* — a function in your app for displaying errors; you probably already have one. * *id* — the workflow process ID (in this example it matches the document ID and is taken from the URL). * *selectedUser* — in the sample code this value comes from a dropdown of usernames. As mentioned earlier, you might not need the user ID at all if you retrieve it from the backend context. * *apiUrl* — the base URL of your backend application. ``` import {Form, FormKey, FormsViewer} from '@optimajet/workflow-forms-viewer' ... export function UserForm({apiUrl}: AppProps) { const {id} = useParams() const {selectedUser} = useAppState() const showError = useShowError() const getForm = useCallback(async (formKey: FormKey) => { ... }, [apiUrl]) const getForms = useCallback(async () => { ... }, [apiUrl, id, selectedUser]) const saveForm = useCallback(async (processId: string, formKey: FormKey, data: Record) => { ... }, [apiUrl, selectedUser]) const executeForm = useCallback(async (processId: string, formKey: FormKey, commandName: string, data: Record) => { ... }, [apiUrl, selectedUser]) return } ``` In short, you need to implement four functions: * *getForm* — calls the controller’s *GetForm* method described above. * *getForms* — calls the controller’s *GetForms* method described above. * *saveForm* — calls the controller’s *SaveForm* method described above. * *executeForm* — calls the controller’s *ExecuteForm* method described above. Four functions on the client, four on the server—nothing too complicated. Next, pass these functions to the *FormsViewer* component from the *@optimajet/workflow-forms-viewer* library, which you can install by running the command. ``` npm install @optimajet/workflow-forms-viewer ``` A sample implementation of the functions is as follows. Function *getForm*. ``` const getForm = useCallback(async (formKey: FormKey) => { const formVersion = typeof formKey.formVersion === 'number' ? `&formVersion=${formKey.formVersion}` : '' const response = await fetch(`${apiUrl}/reports/forms/form?formName=${formKey.formName}${formVersion}`) if (response.ok) return ((await response.json()) as Form).formCode const errorText = await response.text() throw new Error(errorText || `HTTP error ${response.status}`) }, [apiUrl]) ``` Function *getForms*. ``` const getForms = useCallback(async () => { const response = await fetch(`${apiUrl}/reports/forms/get?processId=${id}&user=${selectedUser}`) if (response.ok) return await response.json() as Form[] const errorText = await response.text() throw new Error(errorText || `HTTP error ${response.status}`) }, [apiUrl, id, selectedUser]) ``` Function *saveForm*. Note that validation errors are returned with an HTTP 400 (Bad Request) status code. You don’t have to handle them this way in every case—you can adjust the implementation as needed. ``` const saveForm = useCallback(async (processId: string, formKey: FormKey, data: Record) => { const postData = { formKey: formKey, processId: processId, user: selectedUser, data: data } const response = await fetch(`${apiUrl}/reports/forms/save`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(postData) }) if (response.ok) return {formData: await response.json()} if (response.status === 400) return {formErrors: await response.json()} const errorText = await response.text() throw new Error(errorText || `HTTP error ${response.status}`) }, [apiUrl, selectedUser]) ``` Function *executeForm*. Note that validation errors are returned with an HTTP 400 (Bad Request) status code. You don’t have to handle them this way in every case—you can adjust the implementation as needed. ``` const executeForm = useCallback(async (processId: string, formKey: FormKey, commandName: string, data: Record) => { const postData = { formKey: formKey, processId: processId, commandName: commandName, user: selectedUser, data: data } const response = await fetch(`${apiUrl}/reports/forms/execute`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(postData) }) if (response.ok) return {wasExecuted: (await response.json()).wasExecuted} if (response.status === 400) return {formErrors: await response.json()} const errorText = await response.text() throw new Error(errorText || `HTTP error ${response.status}`) }, [apiUrl, selectedUser]) ``` ### Connect the Forms Manager component[​](#connect-the-forms-manager-component "Direct link to Connect the Forms Manager component") On the client side, in the application available to administrators or power users, implement a component that renders the form editor. Important points: * *showError* — a function in your app used to display errors (you likely already have one). * *licenseKey* — the license key for Form Engine (yes, it’s a separate product and requires its own license). * *apiUrl* — the base URL of your backend application. ``` import {FormsManager} from '@optimajet/workflow-forms-manager' export function FormsManagerPage({apiUrl, licenseKey}: AppProps) { const showError = useShowError() return } ``` It’s straightforward, but keep in mind that the apiUrl of the FormsManager component must ultimately point to the controller that exposes the workflow-scheme designer API. The FormsManager component lives in the *@optimajet/workflow-forms-manager* library, which you can install by running the command. ``` npm install @optimajet/workflow-forms-manager ``` Once integration is complete, the next section will explore the capabilities of the Forms Plugin. --- # Forms Plugin Features In this section, we’ll focus on the settings and usage of the *Forms Plugin*. ## Forms Plugin Settings[​](#forms-plugin-settings "Direct link to Forms Plugin Settings") You connect the plugin to the Workflow Runtime as follows: ``` var formsPluginSettings = new FormsPluginSettings { FormsManagerUrl = configuration.Workflow.FormsManagerUrl, }; var formsPlugin = new FormsPlugin(formsPluginSettings); WorkflowRuntime runtime = new WorkflowRuntime() .WithPlugin(formsPlugin) ... ``` Let’s go through all the settings you can define here and what they’re for. * *FormsManagerUrl* — links the process-scheme designer with the *Forms Manager*. In short, this setting is only needed so that **this button** works. When you click it, the form editor opens with the form that has the selected name and version preloaded. ![forms\_004](/documentation/assets/images/forms_004-087f3c1a363086ba24cc5a53c8909efe.png) * *FormsProcessParameterName* — the name of the [process parameter](https://workflowengine.io/documentation/scheme/parameters) into which the list of forms currently available at that point in the specific process is written. * *FormVersionPropertyName* — the name of the field in the form data where the version of the opened document is stored for optimistic concurrency control. * *DefaultFormDefinition* — the definition of an empty form. This setting is only needed if you integrate your own forms engine; set it to whatever value your engine expects for a form with no fields. * *FormValidator* — the server-side form validator. Server-side validation will be described later. ## Using Forms in Processes[​](#using-forms-in-processes "Direct link to Using Forms in Processes") To display a form in your process, either call the *FormsPlugin.ShowForm* action or use the *ShowForm* activity and provide their specific settings. A single invocation (one action or one activity) shows only one form. To display two forms at the same time, either configure multiple *FormsPlugin.ShowForm* actions in sequence within one implementation or use [subprocesses](https://workflowengine.io/documentation/execution/subprocesses). The following rule applies: forms are available only while the process is in the *Idled* status at the activity where the *FormsPlugin.ShowForm* action was invoked (or while it is in the *ShowForm* activity). ![forms\_005](/documentation/assets/images/forms_005-e113bcecbcdd5f0597e27576aec9601f.png) ![forms\_006](/documentation/assets/images/forms_006-968e955e6212d865c99f8d35429784ea.png) To display a form, you need to specify several settings. We’ll cover them in more detail below. ![forms\_007](/documentation/assets/images/forms_007-95add34b63ab3443fb3beeefe4a9e32d.png) 1. **Form name and version** — specifies which form to show. The default version is always *latest*, but you can also set an explicit version. 2. **Process parameters** in which the form data is stored. 3. **Commands executed together with the form.** Access to a command is equivalent to access to a form; in other words, if a user has access to a command, all forms that mention this command in this list will be available to them. 4. **Command name.** 5. **Form save strategy when executing a command.** Two options are available: validate the form and, if it’s valid, execute the command and save; or execute the command without saving the form data. Let’s dive into these settings and the mechanics of displaying and executing forms. ### Form Name[​](#form-name "Direct link to Form Name") ![forms\_008](/documentation/assets/images/forms_008-9726a52fc72407a0dbd1436ddb8f38ca.png) For a specific user to see a form in the browser, the following conditions must be met: * The process must be waiting for a command. * In the activity where the process is waiting for a command, the *FormsPlugin.ShowForm* action must be invoked. You can invoke this action multiple times within the same activity; this lets you show multiple forms to one user, different forms to different users, or both. * The command being awaited must be linked to the form being displayed (see below). * The command must be available to the user. If all these conditions are met, the form will be displayed. ### Form Parameters[​](#form-parameters "Direct link to Form Parameters") In the form-parameter settings, you need to specify the names of three parameters. They don’t have to be different; they can be the same. Moreover, when they’re the same, that’s the default option. * Initial form data — the form’s initial data * Intermediate form data — the form’s intermediate data, where it’s saved when you click *Save* * Output form data — where the form data is saved after the command associated with the form has been executed After the *FormsPlugin.ShowForm* action is invoked, the value of the *Initial form data* parameter is copied into *Intermediate form data*. The form displays exactly the data stored in *Intermediate form data*, and the *Save* button also writes to it. If we execute the command associated with the form, then the form data (provided the command succeeds) is saved to the *Output form data* parameter. Note again that by default all three parameters are the same, so everything can be handled with a single parameter. ![forms\_009](/documentation/assets/images/forms_009-1e2336f902cb1fca7814316922fe9948.png) ### Linking a Form to Commands[​](#linking-a-form-to-commands "Direct link to Linking a Form to Commands") Access to forms is defined via commands, and any commands associated with a form will be available on that form. In the example below, by invoking *FormsPlugin.ShowForm* twice, you can show the user two forms with different commands. ![forms\_010](/documentation/assets/images/forms_010-885d34d25e2646c485e324217225c3e2.png) User access to forms is configured through [restrictions](https://workflowengine.io/documentation/scheme/rules) associated with command transitions. The rule is simple: if a user has access to at least one command linked to a form, the form is available to them. If the user has access to none of the commands linked to the form, the entire form is unavailable. The example below shows which set of forms each of the three users will see. ![forms\_011](/documentation/assets/images/forms_011-718ea1b7d222114f240039851fb12e7e.png) ## Binding Form Data to Form Fields[​](#binding-form-data-to-form-fields "Direct link to Binding Form Data to Form Fields") The server returns form data as JSON. This JSON may contain both simple properties and nested objects. The binding rule is straightforward: a form field’s key must exactly match the corresponding property name in the returned data object, including a case (case-sensitive). ``` "formData": { "id": "888adc2d-6670-4fd5-be48-940d5b267c40", "submittedBy": "Paula", ... }, ``` ![forms\_012](/documentation/assets/images/forms_012-dbc73f730390583084497314c8d6ed60.png) ## Server-Side Form Validation[​](#server-side-form-validation "Direct link to Server-Side Form Validation") In the *Forms Plugin* settings, you can specify a server-side form validator. ``` var formsPluginSettings = new FormsPluginSettings { FormsManagerUrl = configuration.Workflow.FormsManagerUrl, FormValidator = new ServerSideFormValidator() }; ``` Here’s what such a validator might look like. ``` public class ServerSideFormValidator : IFormValidator { public async Task ValidateSaveAsync(FormKey formKey, DynamicParameter formValue, CancellationToken token = default) { return await ValidateFormAsync(formKey, formValue, token); } public async Task ValidateExecuteAsync(FormKey formKey, WorkflowCommand command, DynamicParameter formValue, CancellationToken token = default) { return await ValidateFormAsync(formKey, formValue, token); } private Task ValidateFormAsync(FormKey formKey, DynamicParameter formValue, CancellationToken token = default) { FormData typedFormData = formValue.ConvertTo(); Dictionary errors = new(); //Validation and error population if (errors.Any()) { return Task.FromResult(new FormValidationResult { IsValid = false, Errors = errors.ToImmutableDictionary() }); } return Task.FromResult(FormValidationResult.Valid); } } ``` Using this validator, you can: * Write different validation logic for different forms. Use *formKey.FormName* to get the form name. * Write different validation logic for saving vs. executing a form. Put the logic in *ValidateSaveAsync* for save validation and in *ValidateExecuteAsync* for execution validation. If you need different logic per command, you can get the command name from *command.CommandName*. It’s important to remember that the same rule used for data binding is used for error binding. The key of the form field must case-sensitively match the property name in the returned error object. ## Forms and Subprocesses[​](#forms-and-subprocesses "Direct link to Forms and Subprocesses") Forms can be used together with subprocesses. For example, you can create a form within a [subprocess](https://workflowengine.io/documentation/execution/subprocesses) that is available at any point in the main process’s lifetime. For a deeper dive, study the sample project, which is examined in detail in the next section. --- # Sample project ## Getting Started[​](#getting-started "Direct link to Getting Started") Clone the [GitHub repository](https://github.com/optimajet/workflow-forms-sample). In the root README, you’ll find instructions on how to run the application. In short, this is a highly simplified sample app where a developer submits a weekly work report, and their manager approves, rejects, or sends the report back for revision. Here’s a schematic of the process. In each state, a specific form is shown, and only the necessary fields are editable. For example, the manager can write their review but can’t modify what the developer wrote. The process also allows the developer to move the report to the *Deleted* state. ![forms\_013](/documentation/assets/images/forms_013-a4236c32e0e27c070ee4903474eb0ba9.png) The first screen of the application lists all reports relevant to the user: a regular developer sees only their own reports, while a manager sees theirs and those of all developers in their department. ![forms\_014](/documentation/assets/images/forms_014-ee3db07fed945e19d7001954224eb5b9.png) 1. Page with available reports. 2. Page with *Forms Manager*, where you can edit forms. 3. Page with the workflow scheme designer. 4. User selector. The *Forms Manager* page looks like this. ![forms\_015](/documentation/assets/images/forms_015-e8446c1f3b57e63f0e57950d9ef6c8a0.png) Take note of the following. In practice, most forms in this application are very similar. To avoid recreating the same components repeatedly, we use templates. 1. Any form whose name starts with `"T:"` is a template then reused in other forms. 2. In the final (end-user) forms, you configure which fields are editable and which are not by using the *readonly* flag, which is set as a property of the template. And the *Workflow Designer* page looks like this. ![forms\_016](/documentation/assets/images/forms_016-33b1d9536951cf3eba868ce625714eb1.png) It’s straightforward: each activity is configured to display a single form. In its settings, you specify which form to show, which commands govern access to the form, and which process parameter stores its data. Now let’s focus on the key aspects of using the *Forms Plugin*. ## Storing Form Data[​](#storing-form-data "Direct link to Storing Form Data") The sample is set up specifically to show how to store form data in regular—and possibly already existing—database tables. Remember, if you don’t have (or don’t need) database tables, form data can be stored in a standard process parameter, which significantly reduces the amount of work. In this example, the form data is stored in a straightforward table. ``` CREATE TABLE dbo.WeeklyReport ( Id UNIQUEIDENTIFIER NOT NULL CONSTRAINT PK_WeeklyReport PRIMARY KEY, SubmittedBy NVARCHAR(256) NOT NULL, SubmittedOn DATETIME NOT NULL, Name NVARCHAR(1024) NOT NULL, ReviewedBy NVARCHAR(256) NULL, Details NVARCHAR(MAX) NOT NULL, Version INT NOT NULL ); ``` Some data is stored explicitly as columns, while the remaining fields are stored as JSON in the *Details* column. Here’s what the typed DTO on the backend looks like. ``` public sealed class WeeklyReportData : IDynamicParameterCompatible { public Guid Id { get; set; } public string SubmittedBy { get; set; } public DateTime SubmittedOn { get; set; } public string Name { get; set; } public string? ReviewedBy { get; set; } public string Details { get; set; } = "{}"; public int Version { get; set; } public string? StateName { get; set; } public WeeklyReportData() { Id = Guid.NewGuid(); SubmittedOn = DateTime.UtcNow; } public IDictionary GetPropertiesAsDictionary() { ... } public void SetPropertiesFromDictionary(IDictionary properties) { ... } } ``` The two key methods are *GetPropertiesAsDictionary* and *SetPropertiesFromDictionary*, which are part of the *IDynamicParameterCompatible* interface. They let Workflow Engine treat a strongly typed parameter as dynamic and expand additional fields stored in *Details* into properties of the dynamic object. The implementation of these methods is located in [WeeklyReportData](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowLib/WeeklyReportData.cs). Next, to persist data in the database, we need to implement an [External Parameters Provider](https://workflowengine.io/documentation/scheme/parameters#external) that saves and retrieves our document from the DB. Here are the key implementation points: ``` public sealed class ReportParameterProvider : IWorkflowExternalParametersProvider { private WeeklyReportRepository _weeklyReportRepository; public const string ParameterWeeklyProgressReport = "WeeklyReport"; public WeeklyReportParameterProvider(WeeklyReportRepository weeklyReportRepository) { _weeklyReportRepository = weeklyReportRepository; } public async Task GetExternalParameterAsync(string parameterName, ProcessInstance processInstance) { return await _weeklyReportRepository.GetByIdAsync(processInstance.RootProcessId); } public async Task SetExternalParameterAsync(string parameterName, object parameterValue, ProcessInstance processInstance) { if (parameterValue is not WeeklyReportData weeklyReportData) { throw new System.ArgumentException(); } weeklyReportData.Id = processInstance.RootProcessId; await _weeklyReportRepository.UpsertAsync(weeklyReportData); } ... public bool HasExternalParameter(string parameterName, string schemeCode, ProcessInstance processInstance) { return parameterName == ParameterWeeklyProgressReport; } } publi sealed class WeeklyReportRepository { private readonly string? _connectionString; public WeeklyReportRepository(IConfiguration configuration) { _connectionString = configuration.GetConnectionString("Default"); } public async Task UpsertAsync(WeeklyReportData weeklyReportData) { string query = """ ... """; await using var connection = new SqlConnection(_connectionString); await connection.ExecuteAsync(query, weeklyReportData); } public async Task GetByIdAsync(Guid id) { string query = """ ... """; await using var connection = new SqlConnection(_connectionString); return await connection.QuerySingleAsync(query, new { Id = id }); } ... } ``` For simplicity, Dapper is used here. See the implementation details in [WeeklyReportParameterProvider](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowLib/WeeklyReportParameterProvider.cs) and [WeeklyReportRepository](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowLib/WeeklyReportRepository.cs). What this approach to data handling gives us: 1. In our code, we always work with a **strongly typed** object. 2. This object can hold any number of additional properties: add components to the form with keys named however you like, and they’ll be saved in the *Details* column of our table. Here’s a schematic of how this flows from the Form to the DB and back. ![forms\_017](/documentation/assets/images/forms_017-1a93c12680633dc54d511778fba98cde.png) It’s also worth noting that both the database table and *WeeklyReportData* have a *Version* field. This field is used for standard optimistic concurrency control. The Forms Plugin learns about this field through its settings. The *ReportParameterProvider* must also be connected to the Workflow Runtime. ``` var formsPluginSettings = new FormsPluginSettings { ... FormVersionPropertyName = nameof(WeeklyReportData.Version), ... }; var formsPlugin = new FormsPlugin(formsPluginSettings); WorkflowRuntime runtime = new WorkflowRuntime() .WithPlugin(formsPlugin) .WithExternalParametersProvider(weeklyReportParameterProvider) ... ``` ## Forms Controller Implementation[​](#forms-controller-implementation "Direct link to Forms Controller Implementation") You can find the implementation of the forms controller here: [WeeklyReportsFormsController](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowApi/Controllers/WeeklyReportsFormsController.cs). We’ve already covered most of its implementation in [the previous section](https://workflowengine.io/documentation/forms-connection#implement-the-forms-controller). A few notes: * The controller includes an additional *SubmitReport* method that starts the process. It isn’t directly related to forms, but it’s necessary—no process, no forms. ``` [HttpPost] [Route("submit")] public async Task> SubmitReport([FromBody] SubmitReportApiRequest request) { var processId = Guid.NewGuid(); var createdInstanceParams = new CreateInstanceParams("WeeklyReportProcess", processId) { IdentityId = request.User }; await _runtime.CreateInstanceAsync(createdInstanceParams); return Ok(new SubmitReportApiResponse(processId)); } ``` * The *GetForms* method returns the forms available to the user. If none is available, it returns a read-only *View* form so any user can view the document. This illustrates that you can extend the forms list with items that have no commands. Saving for this form is also explicitly disabled. ``` [HttpGet] [Route("get")] public async Task> GetForms([FromQuery] Guid processId, [FromQuery] string user) { ... ImmutableList forms = executableFormsResponse.AsSuccess.Forms; if (!forms.Any()) { GetProcessFormResult processFormResponse = await formsPluginRuntimeApi.GetProcessFormAsync(new GetProcessFormParameters { FormKey = new FormKey { FormName = "View" }, ProcessId = processId, DataParameterName = WeeklyReportParameterProvider.ParameterWeeklyProgressReport }); if (processFormResponse.IsSystemError) { throw new Exception(executableFormsResponse.AsSystemError.Message); } ProcessForm defaultForm = processFormResponse.AsSuccess.Form; return Ok(new[] { new ExecutableForm { ProcessId = processId, FormKey = new FormKey { FormName = "View" }, FormCode = defaultForm.FormCode, FormData = defaultForm.FormData.ToCamelCase(), AllowSave = false } }); } ... } ``` In conclusion, note the calls to *ToCamelCase* and *ToPascalCase*. Their use is mostly cosmetic. As explained [here](https://workflowengine.io/documentation/forms-features#binding-form-data-to-form-fields), data binding is performed by the component key. Since the frontend typically uses *camelCase* and the backend *PascalCase*, you’ll need to normalize names accordingly. ## Additional Setup for the Workflow Runtime[​](#additional-setup-for-the-workflow-runtime "Direct link to Additional Setup for the Workflow Runtime") The Workflow Runtime is initialized in the [WorkflowRuntimeService](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowLib/WorkflowRuntimeService.cs) class. In addition to the *ReportParameterProvider* described above, it also connects the [WeeklyReportActionProvider](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowLib/WeeklyReportActionProvider.cs) and the [WeeklyReportRuleProvider](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowLib/WeeklyReportRuleProvider.cs). *WeeklyReportActionProvider* contains two [actions](https://workflowengine.io/documentation/scheme/actions): * *InitReport* — creates the report object and records who submitted the report and the submission date. * *SetReviewer* — records which manager processed the report. It also contains one [condition](https://workflowengine.io/documentation/scheme/conditions): * *CanDelete* — defines the rules for deleting the document; the document can be logically deleted in any state other than the final ones. *WeeklyReportRuleProvider* is the standard provider for authorizing access to commands. For details on how this works, see [here](https://workflowengine.io/documentation/scheme/rules). It declares three authorization rules: * *Author* — whether the user is the document’s author. * *CheckRole* — checks the role; the relevant role here is *Manager*. * *CheckDivision* — checks whether the authorized user works in the same department as the document’s author. All users and roles, for educational purposes, are defined in [Users](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowLib/Users.cs). ## Server-Side Validation[​](#server-side-validation "Direct link to Server-Side Validation") The sample uses server-side validation. The validator code is defined in [WeeklyReportValidator](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowLib/WeeklyReportValidator.cs). It always checks whether *WhatWasDone* and *EncounteredProblems* are filled in, and whether *ManagerReview* is filled in when the document is in the *Review* state. Client-side validation is not configured in the forms, but you can add it if needed; it will then work in tandem with the server-side validation. Client-side checks are run before attempting to submit to the server. ## Implementing Forms on the Frontend[​](#implementing-forms-on-the-frontend "Direct link to Implementing Forms on the Frontend") On the frontend, the primary file of interest is [WeeklyReportPage](https://github.com/optimajet/workflow-forms-sample/blob/master/frontend/src/components/WeeklyReportPage.tsx). This is where the form is rendered to the user, and we’ve already detailed all the methods you need to implement [here](https://workflowengine.io/documentation/forms-connection#connect-the-forms-viewer-component). In [FormsManagerPage](https://github.com/optimajet/workflow-forms-sample/blob/master/frontend/src/components/FormsManagerPage.tsx), the *Forms Manager* is connected as described [here](https://workflowengine.io/documentation/forms-connection#connect-the-forms-manager-component). Note also that the *Forms Manager* works through the [DesignerController](https://github.com/optimajet/workflow-forms-sample/blob/master/backend/WorkflowApi/Controllers/DesignerController.cs), which you must implement anyway for the workflow scheme designer to operate. ## Workflow Scheme Details to Pay Attention To[​](#workflow-scheme-details-to-pay-attention-to "Direct link to Workflow Scheme Details to Pay Attention To") Here we’ll outline several specifics in the process scheme that are worth noting—these are the elements that make the application behave exactly as we need. ![forms\_018](/documentation/assets/images/forms_018-f4e09474f655f7e33dd21297223f8fd2.png) 1. In *Activity Implementation*, we specify which form should be displayed and which commands it’s associated with. 2. In *Transition Restrictions*, we define access to the command—for example, in the screenshot it’s available to the manager of the author’s department. Through this command access, we also determine access to the form. ![forms\_019](/documentation/assets/images/forms_019-fc9edf8ce3f5eb421eec38c1452062ec.png) The report’s author can delete their report in any state except the final one. This is implemented via a [subprocess](https://workflowengine.io/documentation/execution/subprocesses). Note that the transition specifies not only that the command is available exclusively to the report’s author, but also the condition we mentioned earlier: the report can be deleted only when it is not in a final state. --- # How to choose the right embedded workflow automation tool? info The author of this article is Denis Kotov. Using the course payment process as an illustration, this post will compare Workflow Engine with Camunda 7, as a solution. Any developer in an enterprise environment has faced the task of automating business processes. There are dozens of solutions to this issue as well as hundreds more ways to cope with it. We will discuss specific tools in a practical task today. ## What is business process automation?[​](#what-is-business-process-automation "Direct link to What is business process automation?") Business process automation is often presented through marketing, so it might be difficult to understand what it means. I've mostly seen two implementations: 1. Sequential or parallel execution of blocks of code when saving the intermediate state and execution history of these pieces of code (with logic) to the database. The possibility to handle the change of these sequences correctly. 2. The same idea, but in addition to calling external systems that already use complicated logic, rather than just executing blocks of code. This is called microservice orchestration. In 100% of cases, visualization of what is happening is required, because the sequence of these blocks is perceived as a business process. 80% require to visualize the change of these blocks without involving programmers. If visualization and changing pieces of code are not required, then in general, programmers might have several alternatives: * Chain of Responsibility Pattern; * State machine pattern; * Using Petri Nets; * Our any `if-else` and `switch-case`; * Another in-house development variant. Furthermore, it is worth mentioning BPMN that usually can complement business process automation tasks. BPMN is an XML file format that contains a list of visual abstractions that are mapped into it and how they interact with one another. Because BPMN is well-known, well-described, and has set conventions about style, a programmer will be able to load such files into his application and execute them correctly, thus resolving the problem of visualization clarity. ## An example of a task that BPM engines solve[​](#an-example-of-a-task-that-bpm-engines-solve "Direct link to An example of a task that BPM engines solve") Business-wise, automation activities frequently resemble the following: > **The process of buying online courses must be automated.** > > Text description: > > * A user selects the course to pay for; > * Enters an e-mail; > * Clicks "pay"; > * Website displays a modal window with a page from acquiring; > * The user makes the payment; > * Acquiring sends payment confirmation; > * The system sends a notification to the user by e-mail with the details of access to the online course; > * If the user has not made the payment within 10 minutes after opening the modal window, it is necessary to send the user an email with a promo code. In BPMN format it will look like this: ![BPMN process example](/documentation/assets/images/bpmn-process-example-1a538d758a08ac4e73a5f2ae0c66ba6f.png "BPMN process example") Here, it's crucial to visualize the data and modify the squares' order using the graphical user interface (GUI). This problem can be solved by a developer implementing a tool that enables: * Visually create a similar diagram; * Attach pieces of code to each square; * Save the variables of the ongoing process (email, payment amount, promotional code, and so on); * Create tests for all output; * Run this in docker or kubernetes; * Check that everything is "alive" on the production environment; * Be able to migrate current processes to a new scheme version when it becomes available. The typical developer method can be used to complete all of this (CI/CD, feature flags, code reviews, and so on). ## Choose the right workflow automation tool[​](#choose-the-right-workflow-automation-tool "Direct link to Choose the right workflow automation tool") I chose to assess two tools in this article. One of them is Camunda 7 from JVM world - it is implemented in a variety of projects, but sometimes it may be complicated for users without expertise. The second one comes from the .NET world - it's the opposite to JVM. It turned out that the Workflow Engine is one of the few embedded C# engines. I've chosen a traditional approach for comparing both tools by considering Use Cases and BPMN support: 1. **Use Case** - often engines can be delivered as a standalone container or even a separate application, and sometimes as libraries. As a developer, I prefer the second option because I don't need to overhaul my CI/CD pipelines and can continue to use my existing application elements without the annoying integration with a standalone container. 2. **BPMN support** - some engines offer support for BPMN, a formal standard for describing business processes. This standard is very powerful and quite complex. There are dozens of tools that produce a description of business processes in this format, and it would seem that they can be reused. However, practice has proven that BPMN alone is never sufficient for automation because each unique system will have its own features. For instance, Camunda does not support the cycle element, but BPMN does. Therefore, just because one engine supports BPMN doesn't mean that your business processes in this format can be quickly and easily automated in that engine. Let's compare Camunda 7 and Workflow Engine: | № | Parameter name | Workflow Engine | Camunda 7 | | -- | --------------------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------ | | 1 | Programming language | C#, .NET | JVM Based - Java, Kotlin etc. | | 2 | Integration options | Embedding | Embedding, REST API | | 3 | BPMN support | No | Yes | | 4 | Supported databases | MySQL, Oracle, PostgreSQL, SQL Server (Azure SQL), MongoDB | MySQL, Oracle, PostgreSQL, SQL Server, H2, CockroachDB | | 5 | Documentation | Extensive | Extensive | | 6 | Number of stars on GitHub | 700+ | 3000+ | | 7 | Community | Small | Big | | 8 | Horizontal scaling | Yes, through the database | Yes, through the database | | 9 | Framework for testing | No | Yes | | 10 | Administration Application | No | Yes | | 11 | Is there a completely free license option for production? | Yes | Yes | | 12 | The cost of paid licenses | $, one-time | $$$$, annually | Between these two technologies, there is essentially no difference. If you have C#, then in general you could use Camunda through the REST API, but this is significantly time-consuming and more complicated than using Workflow Engine. If you have a JVM, then you simply cannot use Workflow Engine (you can use a Workflow Server based on Workflow Engine via REST API, but this is a different product). ## Let us compare Camunda 7 and Workflow Engine[​](#let-us-compare-camunda-7-and-workflow-engine "Direct link to Let us compare Camunda 7 and Workflow Engine") ### Start of developing process[​](#start-of-developing-process "Direct link to Start of developing process") Since both of these engines can be embedded, the fundamental requirement for both embeddings is the inclusion of vendor libraries in your program. Camunda 7 has several libraries - the engine itself, REST API to the engine, web applications for administration. All libraries can be used separately. When adding a dependency, it looks like this (for a typical Spring application): ``` org.camunda.bpm.springboot camunda-bpm-spring-boot-starter-webapp 3.2.0 ``` The Camunda database's table is created on its own. The Workflow Engine also has several libraries - the engine itself (the REST API for Designer is already built into it), a provider for a specific database. Integration looks like this: ``` dotnet add package WorkflowEngine.NETCore-Core --version 7.2.0 dotnet add package WorkflowEngine.NETCore-ProviderForMSSQL --version 7.2.0 ``` Or in your `.csproj` file: ``` ``` Database tables can be created by running the SQL scripts that are provided by the vendor. ### Create a process diagram[​](#create-a-process-diagram "Direct link to Create a process diagram") Both tools - Camunda and Workflow Engine - allow visual process editing. Camunda 7 provides a package for all operating systems called Camunda Modeler. Essentially, it can generate a .xml file according to the BPMN standard and send it through the REST API to Camunda. ![Camunda Modeller](/documentation/assets/images/camunda-modeller-d0cdf643152af79a2912452070cfb4dd.png "Camunda Modeller") Workflow Engine provides ready-made NPM packages for embedding into various JS frameworks such as Angular or React. The meaning is also the same - the visual scheme is described by XML and sent to the engine through the designer. Naturally, I would prefer to have a ready-made application from the vendor here that is always current and functions as intended (it is available for Workflow Server solution, then, this is a different product). This is the Workflow Engine Designer interface: ![Workflow Engine Designer](/documentation/assets/images/workflow-engine-commands-65d30a48cb791033e5a50cb1a19bb698.png "Workflow Engine Designer") In the Workflow Engine, the process for paying for courses would resemble this: ![Workflow Engine Course process](/documentation/assets/images/workflow-engine-course-process-46b031ba7f4f3e4a4a36e44aa630cd3a.png "Workflow Engine Course process") ### Basic abstractions and their storage in the database[​](#basic-abstractions-and-their-storage-in-the-database "Direct link to Basic abstractions and their storage in the database") At this point, significant disparities between the two companies' strategies start by weighing benefits and drawbacks. #### Camunda tables[​](#camunda-tables "Direct link to Camunda tables") There are complex abstractions to implement BPMN in Camunda, you be the judge! ![Camunda tables](/documentation/assets/images/camunda-tables-77dc0f0a7e6fae563558d4a81c829529.png "Camunda tables") * Process definition - schema in XML; * Job definition - specific job to be executed; * Process instance - a specific running process; * Activity - a square inside a process; * Variables - process variables; * Job - specific running job; * Execution - analogue of a token in Petri nets. All of this data is also kept for history, including changes to the specified abstractions along with process execution. It can be difficult to deal with this because of the one-to-many relationship and the BPMN notation, in which these abstractions are not explicitly emphasized (but are built by Camunda). To be completely honest, these abstractions are included for a reason, and this is arguably the most obvious method to embrace BPMN. #### Workflow Engine tables[​](#workflow-engine-tables "Direct link to Workflow Engine tables") Compared to Camunda, this solution is much simpler in every way: * [WorkflowProcessScheme](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessScheme) - analogue of Process definition; * [WorkflowProcessInstance](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstance) - is analogous to process instance, but the current state is stored right there; * [WorkflowProcessInstancePersistence](https://workflowengine.io/documentation/db-entities/tables/WorkflowProcessInstancePersistence) - is analogous to process variables. The same data, including that related to process execution, is recorded for history. Other configuration data (permissions, global values, and so on) are also stored by each solution, but this information is less interesting. #### Process abstractions in Camunda[​](#process-abstractions-in-camunda "Direct link to Process abstractions in Camunda") What specific information is obtained when processes are formed is also crucial with regard to data saving. In Camunda, this is a full-fledged, 98% working BPMN, described in the OMG standard. This means that there are about 400 semantic constructs and about 12 key abstractions. ![BPMN events](/documentation/assets/images/bpmn-events-1-621443f97b254067000ebf34d40e5a75.png "BPMN events") #### Process abstractions in Workflow Engine[​](#process-abstractions-in-workflow-engine "Direct link to Process abstractions in Workflow Engine") In this solution, everything is quite simple - there are only two main abstractions - `Activity` and `Transition`. ![Workflow Engine abstractions](/documentation/assets/images/workflow-engine-abstractions-1addb004330c864baf8b27bec9d1d6d1.png "Workflow Engine abstractions") ### Delegating code execution[​](#delegating-code-execution "Direct link to Delegating code execution") Both solutions allow you to call your application code. To accomplish this, there must be a connection between the code and the XML schema. In Camunda, this is done by specifying a way to find the code and its unique identifier (in this case, the name of the Bean in the context of Spring): ![Code execution in Camunda](/documentation/assets/images/camunda-code-execution-dabf596837b8eca74d37b51e76b7711e.png "Code execution in Camunda") This piece of code is added to the application in this manner. Keep in mind that the `JavaDelete` interface must be implemented with the required `execute` method, which receives a `DelegateExecution` object that gives access to any engine resources we might require. ``` package bpmn.payment.delegate import bpmn.payment.component.EmailSender import org.camunda.bpm.engine.delegate.DelegateExecution import org.camunda.bpm.engine.delegate.JavaDelegate import org.springframework.stereotype.Component @Component("sendEmailDelegate") class SendEmailDelegate(val emailSender: EmailSender) : JavaDelegate { override fun execute(execution: DelegateExecution) { val email = execution.getVariable("email") as String val emailId = Integer.parseInt(execution.getVariable("emailId") as String) emailSender.sendEmail(email, emailId) } } ``` Similar principles apply in Workflow Engine, however the pieces must be manually registered. In Camunda the registry of unique pieces of code is done by Spring with its DI. ``` public ActionProvider() { // Register your actions in _actions and _asyncActions dictionaries _actions.Add("MyAction", MyAction); // sync _asyncActions.Add("MyAsyncAction", MyAsyncAction); // async _actions.Add("Mamad", Mamad); // sync _asyncActions.Add("AsyncMamad", AsyncMamad); // async _actions.Add("Update", Update); // sync _asyncActions.Add("AsyncUpdate", AsyncUpdate); // async // Register your conditions in _conditions and _asyncConditions dictionaries _conditions.Add("MyCondition", MyCondition); // sync _asyncConditions.Add("MyAsyncCondition", MyAsyncCondition); // async } ``` ![Workflow Engine actions](/documentation/assets/images/workflow-engine-actions-demo-dad993e15fa67a807a9b409d83b649e1.png "Workflow Engine actions") Similarly, within our code, we get `processInstance` and runtime objects, from which we can retrieve all data. An advantage is that, unlike Camunda, where you need to know the precise name of the bean, the Workflow Engine allows you to get a list of registered actions. On the other hand, Camunda has a built-in runtime executor of conditions on gateways, for instance, the following expression can be written in XML: ![Camunda gateway condition](/documentation/assets/images/camunda-gateway-condition-9cfaa6235bf779885a4aa332bc49982a.png "Camunda gateway condition") Moreover, it can be validated without additional effort from the development side. However, I personally think it's bad practice because it's challenging to test such expressions, and it could be challenging to comprehend precisely how variables are used there. In Workflow Engine, we can declare Conditions in the same way just as an Action. This approach is more explicit and more testable. Conditions can also be added to Transitions: ![Workflow Engine transition expression](/documentation/assets/images/workflow-engine-transition-expression-040d7eab7b0f6ecf72168bfc8f4d2e30.png "Workflow Engine transition expression") In the browser, you may also easily write code. It is technically possible to create a test for it that could then be added to the CI pipeline, but I do not recommend it. ### Writing a Test[​](#writing-a-test "Direct link to Writing a Test") It is obvious that each component of the process — whether an Action or a Bean — can be tested independently. In both solutions, it is considered a good practice not to write big logic inside, and using an interlayer between the engine and the real business logic. Business logic ought to be separated into services or components already. This approach will facilitate modularity, usability, and testability. In addition, I would like to test the process itself and the code. In this case, Camunda provides a library that allows you to raise the engine itself, load diagrams into it, run in-memory database and executing processes step by step by comparing the expected behavior with the one received from the engine. ``` @Test @Deployment(resources = ["BPMN/Origination.bpmn"]) fun TechErrorPath() { // Given val pi = startProcess() assertThat(pi).isWaitingAt("Task_0w7obdg") // When execute(job()) // Then assertThat(pi).isWaitingAt("ServiceTask_1sz8j3l") execute(job()) assertThat(pi).isEnded } ``` In the Workflow Engine you can use the same approach using [MSTest](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest). ### Process context[​](#process-context "Direct link to Process context") When a process is executed, it retrieves the data. In my example of paying for courses, my context consists of email, selected course, promo code, course cost, and so on. This information must be recorded and made available to my pieces of code. In both solutions, you can get them through an object passed to my class - in the case of Camunda, this is `DelegateExecution`, and in the case of WorkflowEngine, this is `processInstance`. In Camunda it looks like this: ``` @Component("addToSegmentDelegate") class AddToSegmentDelegate(val emailSender: EmailSender) : JavaDelegate { override fun execute(execution: DelegateExecution) { execution.setVariable("paymentStatus", "ok") val contactId = emailSender.findOrInsertContact(execution.getVariable("email") as String) val segmentId = Integer.parseInt(execution.getVariable("segmentId") as String) emailSender.addToSegment(contactId, segmentId) execution.setVariable("paymentOk", true) } } ``` In Workflow Engine it will be: ``` var stringParameter = processInstance.GetParameter("StringParameter"); var objectParameter = processInstance.GetParameter("ObjectParameter"); ``` Unlike Camunda, which stores such variables in a Named-Value wide table for ALL instances and frequently causes performance issues with rapid context growth, Workflow Engine allows you to pass your own context provider, which controls how and where such variables are persisted. ### Deploy process diagram[​](#deploy-process-diagram "Direct link to Deploy process diagram") Each solution offers two ways to deploy a scheme - through the API or using the designer (which is essentially the same). Both solutions provide the automatic deployment of XML from project resources, allowing you to store processes in Git and carry out procedures for comparing and approving XML versions. ### Application launch[​](#application-launch "Direct link to Application launch") With Camunda or the Workflow Engine, I can have a conventional application for a specific language and do whatever I like - thanks to all the magic of embedded engines - wrap a fat jar or exe (or assembling it in the .NET world) into docker, deploy to kubernetes etc. I didn't notice any issues or inconsistencies with these two engines. ### Monitoring and operation[​](#monitoring-and-operation "Direct link to Monitoring and operation") Both solutions give users the ability to see instances' current states, modify their variables, and more. In the case of Camunda the historical data (i.e. on processes that have completed) is available only in the paid version. The current state of the instance in Workflow Engine is indicated as follows: ![Process status in Workflow Engine](/documentation/assets/images/process-status-in-workflow-engine-26a5b821867cb197e755e6431912dd31.png "Process status in Workflow Engine") Below the current state of all processes in Camunda 7: ![Statuses of processes in Camunda](/documentation/assets/images/statuses-of-processes-in-camunda-6be3b331f652bf506986c9d50dead9fb.png "Statuses of processes in Camunda") ### Process schema update and instance migration[​](#process-schema-update-and-instance-migration "Direct link to Process schema update and instance migration") When changing the version of the process (let's say a process element was added), there are several options in each tool: * Do nothing - old processes will be completed as usual, new ones will be initialized from the new version; * Migrate instances to the new version. This is accomplished in Camunda via a special migration REST API (or an internal API supplied by the engine), where the old and new versions should be specified. In Workflow Engine, we can deprecate obsolete schemas and run automatic migration, which generates events that we can process to implement business logic (enrichment with new data, changing states, and so on): ``` runtime.OnSchemaWasChangedAsync += async (sender, args, token) => { var pi = await runtime.GetProcessInstanceAndFillProcessParametersAsync(args.ProcessId); if (!pi.ProcessScheme.Activities.Any(a => a.Name == pi.CurrentActivityName)) { var initialActivity = pi.ProcessScheme.InitialActivity; pi.CurrentActivityName = initialActivity.Name; await runtime.PersistenceProvider.UpdatePersistenceStateAsync(pi, TransitionDefinition.Create(initialActivity, initialActivity)); } }; ``` ### Performance[​](#performance "Direct link to Performance") The approach of coping with loads is a crucial requirement. Unfortunately, neither Camunda nor the Workflow Engine have the most recent information available on this. Overall, the number of instances per second, or operations per second, is generally rated in BPM engines rather than RPS. On a high-performance server, Workflow Engine support [250 operations per second](https://workflowengine.io/blog/workflow-performance/). Camunda supports 150-200 [Process Instances](https://camunda.com/blog/2014/01/benchmarking-camunda-process-engine/) per second on similar hardware. Keep in mind that if you need about RPS 1000+ from the engine to process handling, then probably these are not "business processes" and you may require another tool, such as Apache Spark or Flink. ### Learning curve[​](#learning-curve "Direct link to Learning curve") Before using the tool effectively, it's vital to take into account the level of abstraction, which includes the information review and objects that need to be modelled and implemented. I am involved in this because I have been programming in Kotlin for a long time, and the last time I wrote in .NET was 8 years ago. Based on my experience, a middle-level developer can learn the fundamentals of the Workflow Engine from scratch in three to five weeks. It may require 2-3 months to learn Camunda and BPMN from scratch. Camunda and BPMN are quite complex. ## Conclusion[​](#conclusion "Direct link to Conclusion") I sincerely hope this article assisted you in making the right decision and in learning more about engine selection. Timers, conditional jumps, working with retries, errors, and other features have not all been studied by me, but they are not crucial for selecting an engine, and in my opinion, both systems are developed in this area. The guidelines for choosing are fairly straightforward: if you're on .NET, you choose Workflow Engine. If you're running on the JVM - use Camunda (and get ready for a three-month BPMN dive). --- # Framework-agnostic Setup info Also check out the [integration tips](https://workflowengine.io/documentation/integrations-tips) section. An integrated project is included at [the end of this article](https://workflowengine.io/documentation/how-to-integrate#conclusion). Furthermore, it is possible to download [our samples](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Samples) and see how to manage workflows in web applications. ![Workflow Engine scheme](/documentation/assets/images/scheme-integration-6eefcfdb12e76774850253c95fe45361.png) | # | Components | Description | Documentation | | - | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | 1 | WFE Designer | It is a javascript object on a web page where you want the designer to be displayed, and a function that will process requests from the designer and pass them to WorkflowRuntime. | [Designer](https://workflowengine.io/documentation/main-terms/designer) | | 2 | WFE Runtime | This object allows to create a process, retrieve a list of commands, execute commands, set a process state, etc. It also provides an API for the designer, so you have to create one WorkflowRuntime object in your application or service and call its methods to enable document management functionality. | [Runtime](https://workflowengine.io/documentation/main-terms/runtime), [Basic Operations](https://workflowengine.io/documentation/main-terms/basic-operations) | | 3 | DB Provider | You have to use persistence providers to connect to the process database. Depending on your database type, you have to create an object and pass it to WorkflowRuntime. | [Persistence](https://workflowengine.io/documentation/main-terms/persistence) | | 4 | Workflow tables | A set of database tables. It's needed for workflow storage. | [Persistence](https://workflowengine.io/documentation/main-terms/persistence) | | 5 | IWorkflowRuleProvider | Provides for security integration. It allows you to call the available authorization methods, or write new ones. Rules represent the functions that are called at a certain moment in time. | [Rules](https://workflowengine.io/documentation/scheme/rules) | | 6 | IWorkflowActionProvider | Provides for business logic integration. It allows you to call your own business functions for each step of the workflow. | [Actions](https://workflowengine.io/documentation/scheme/actions) | This article describes an example for `ASP.NET Core MVC web application` and `MS SQL` database. Integration with solutions based on other technologies (ASP.NET WebForms, .NET WinForms) or databases (MySQL, PostgreSQL, Oracle, MongoDB and others) is roughly the same and should not entail any difficulties. Integrating Workflow Engine into an application takes about an hour and consists of 6 simple steps: 1. Creating an empty solution in Visual Studio or JetBrains Rider. 2. Setting up the database. 3. Initializing WorkflowRuntime. 4. Connecting the Designer. 5. Creating a document workflow scheme. 6. Creating a process and calling commands. If you plan to integrate Workflow Designer into an Angular-based or React-based application, read [the following guide](https://workflowengine.io/documentation/faq/workflow-engine/integrate-workflow-engine-into-angular-or-react). ## 0. Video tutorial[​](#0-video-tutorial "Direct link to 0. Video tutorial") [YouTube video player](https://www.youtube.com/embed/Tqe_LNwEYgU) ## 1. Creating an empty solution[​](#1-creating-an-empty-solution "Direct link to 1. Creating an empty solution") Firstly, it will be required to create an empty solution where will be created a class library project (WorkflowLib) and an ASP.NET Core MVC web application (Model-View-Controller) i.e. the Workflow Designer project. In the preferred IDE, you should choose *'New Solution'* -> *' EmptySolution'*. ## 2. Setting up the database[​](#database "Direct link to 2. Setting up the database") Download our .NET Core persistence provider [here](https://workflowengine.io/downloads/net-core/). Moreover, you might download providers from [GitHub repository](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Providers). ![DownloadPersistenceProvider](/documentation/assets/images/wfe-integrate-1-c4460d495043dd45e9ad7371ec065dfd.png) info Learn more about [persistence](https://workflowengine.io/documentation/main-terms/persistence). ## 3. Initializing WorkflowRuntime[​](#runtime "Direct link to 3. Initializing WorkflowRuntime") 3.1. Create a Class Library project in Visual Studio or JetBrains Rider. It can be called: `WorkflowLib`. Remember Starting with WFE 7.0, the artifacts for the .NET Framework are no longer available. The .NET Framework 4.6.2 is now the minimum supported version (netstandard2.0), except for Oracle Provider, for which the minimum version is .NET Core 3.0 (netstandard2.1). 3.2. [Add](https://docs.microsoft.com/en-us/nuget/tools/package-manager-ui#finding-and-installing-a-package) the following NuGet packages into your project (.NET Framework / .NET Core): * [WorkflowEngine.NETCore-Core](https://www.nuget.org/packages/WorkflowEngine.NETCore-Core/) * [WorkflowEngine.NETCore-ProviderForMSSQL](https://www.nuget.org/packages/WorkflowEngine.NETCore-ProviderForMSSQL/) Once NuGet packets are installed, assemblies will be added to the project. 3.3. Add reference `System.Configuration` (for .NET Framework only). 3.4. Create a `WorkflowInit.cs` file. Add the following namespaces to the `usings` section: ``` using System; using System.Xml.Linq; using OptimaJet.Workflow.Core.Builder; using OptimaJet.Workflow.Core.Bus; using OptimaJet.Workflow.Core.Runtime; using OptimaJet.Workflow.DbPersistence; ``` Add the initialization of the `WorkflowRuntime` object. It should look like this: ``` public static class WorkflowInit { private static readonly Lazy LazyRuntime = new Lazy(InitWorkflowRuntime); public static WorkflowRuntime Runtime { get { return LazyRuntime.Value; } } public static string ConnectionString { get; set; } private static WorkflowRuntime InitWorkflowRuntime() { // TODO Uncomment for .NET Framework if you don't set ConnectionString externally. //ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; if (string.IsNullOrEmpty(ConnectionString)) { throw new Exception("Please init ConnectionString before calling the Runtime!"); } // TODO If you have a license key, you have to register it here //WorkflowRuntime.RegisterLicense("your license key text"); // TODO If you are using database different from SQL Server you have to use different persistence provider here. var dbProvider = new MSSQLProvider(ConnectionString); var builder = new WorkflowBuilder( dbProvider, new OptimaJet.Workflow.Core.Parser.XmlWorkflowParser(), dbProvider ).WithDefaultCache(); var runtime = new WorkflowRuntime() .WithBuilder(builder) .WithPersistenceProvider(dbProvider) .RunMigrations() .EnableCodeActions() .SwitchAutoUpdateSchemeBeforeGetAvailableCommandsOn() .AsSingleServer(); var plugin = new OptimaJet.Workflow.Plugins.BasicPlugin(); // Settings for SendEmail actions // plugin.Setting_Mailserver = "smtp.yourserver.com"; // plugin.Setting_MailserverPort = 25; // plugin.Setting_MailserverFrom = "from@yourserver.com"; // plugin.Setting_MailserverLogin = "login@yourserver.com"; // plugin.Setting_MailserverPassword = "pass"; // plugin.Setting_MailserverSsl = true; runtime.WithPlugin(plugin); // events subscription runtime.OnProcessActivityChanged += (sender, args) => { }; runtime.OnProcessStatusChanged += (sender, args) => { }; // TODO If you have planned to use Code Actions functionality that required references to external assemblies // you have to register them here //runtime.RegisterAssemblyForCodeActions(Assembly.GetAssembly(typeof(SomeTypeFromMyAssembly))); // starts the WorkflowRuntime // TODO If you have planned use Timers the best way to start WorkflowRuntime is somewhere outside // of this function in Global.asax for example runtime.Start(); return runtime; } } ``` If you have a license key, add a call to the `WorkflowRuntime.RegisterLicense` method with the license key. info You can find more information in this [documentation section](https://workflowengine.io/documentation/main-terms/runtime). ## 4. Connecting the Designer[​](#designer "Direct link to 4. Connecting the Designer") info Starting with Workflow Engine 13.0, you can quickly launch the designer using the `npx` command. To do so, enter the following command in your terminal: ``` npx @optimajet/workflow-designer http:localhost:5000/Designer/API Scheme ``` * `First parameter`: The address of your backend server that hosts the workflow engine. By default, this is set to `https://demo.workflowengine.io/Designer/API`. * `Second parameter`: The name of the schema to connect to. The default is `SimpleWF`. Remember Starting with WFE 7.0, the artifacts for the .NET Framework are no longer available. The .NET Framework 4.6.2 is now the minimum supported version (netstandard2.0), except for Oracle Provider, for which the minimum version is .NET Core 3.0 (netstandard2.1). 4.1. Create an ASP.NET Core MVC web application (choose Model-View-Controller). You just should create a project according to the dotnet template. This project can be called `WorkflowDesigner`. The instructions from Microsoft are the following: * [The instructions for .NET Framework](https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/introduction/getting-started). * [The instructions for .NET Core](https://docs.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/start-mvc). 4.2. [Add](https://docs.microsoft.com/en-us/nuget/tools/package-manager-ui#finding-and-installing-a-package) the following NuGet packages to the project (.NET Framework / .NET Core): * [WorkflowEngine.NETCore-Core](https://www.nuget.org/packages/WorkflowEngine.NETCore-Core/) * [WorkflowEngine.NETCore-ProviderForMSSQL](https://www.nuget.org/packages/WorkflowEngine.NETCore-ProviderForMSSQL/) 4.3. The reference to the class library project: [WorkflowLib from step 3](https://workflowengine.io/documentation/how-to-integrate#runtime), must be added to have access to the `WorkflowRuntime` object. You can connect the dependency as indicated below: ![Connecting Designer](/documentation/assets/images/designer-dependency-a8d77975d518243cb8956013617b66b3.png) 4.4 Then, create a new controller `DesignerController` which will handle the designer's requests to the backend. ``` using System.Collections.Specialized; using System.Text; using Microsoft.AspNetCore.Mvc; using OptimaJet.Workflow; using WorkflowLib; namespace WorkflowDesigner.Controllers { public class DesignerController : Controller { public async Task Api() { Stream? filestream = null; var parameters = new NameValueCollection(); //Defining the request method var isPost = Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase); //Parse the parameters in the query string foreach (var q in Request.Query) { parameters.Add(q.Key, q.Value.First()); } if (isPost) { //Parsing the parameters passed in the form var keys = parameters.AllKeys; foreach (var key in Request.Form.Keys) { if (!keys.Contains(key)) { parameters.Add(key, Request.Form[key]); } } //If a file is passed if (Request.Form.Files.Count > 0) { //Save file filestream = Request.Form.Files[0].OpenReadStream(); } } //Calling the Designer Api and store answer var (result, hasError) = await WorkflowInit.Runtime.DesignerAPIAsync(parameters, filestream); //If it returns a file, send the response in a special way if (parameters["operation"]?.ToLower() == "downloadscheme" && !hasError) return File(Encoding.UTF8.GetBytes(result), "text/xml"); //response return Content(result); } } } ``` In addition, the following method must be appended to the created `DesignerController`: ``` public IActionResult Index() { return View(); } ``` 4.5 Next, the directory called *'Designer'* and a html page: `Index.cshtml`, which the Designer will be opened, must be created. It should be located in the folder *'Views'* according the ASP NET Core architecture approach. Furthermore, the link on the main page to access the Designer should be included in the page: `Index.cshtml` in *'Home'* directory. * In *Designer* directory the following code should be added in `Index.cshtml` : ``` @{ ViewBag.Title = "Designer"; Layout = "~/Views/Shared/_Layout.cshtml"; } ``` ![Connecting Designer](/documentation/assets/images/designer-index-1f1b37608aa5ec4c3407ae1f50207c27.png) * Moreover, in `Index.cshtml` in *Home* directory this code: ``` ``` ![Connecting Designer](/documentation/assets/images/home-index-ba19df246f92c9c266cd84151cd14c07.png) 4.6 Download the frontend artifacts for the Designer to the directory with static content: */wwwroot*. They can be found in the [WorkflowEngine.NET site](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Designer). The folder *'templates'* is required, besides the file: `workflowdesigner.min.css` and `workflowdesigner.min.js` (save them in *'css'* and *'js'* directories respectively). 4.7. The *ConnectionString* should be annexed to the settings and use it at the start of the application. This is required for the Designer API to work. Then, [add a database connection string](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/connection-strings-and-configuration-files) in the *ConnectionStrings* section in `web.config` or `appsettings.json` configuration file. * For .NET Framework: ``` ``` * For .NET Core: ``` { "ConnectionStrings": { "DefaultConnection": "Data Source=(local);Initial Catalog=wfe_sample;User ID=sa;Password=1" } } ``` Be aware Make sure that server address, authentication type, login and password match your settings. Furthermore, the *ConnectionString* initialization for `WorkflowRuntime` is needed. * In case of .NET Framework: Check that the following code in `InitWorkflowRuntime` method from `WorkflowLib` project in `WorkflowInit.cs` is uncommented: ``` ConnectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; ``` * For .NET Core: Insert the following code in `Program.cs`: ``` using WorkflowLib; ... //Defining connection settings WorkflowInit.ConnectionString = app.Configuration.GetConnectionString("DefaultConnection"); ``` 4.8 The main Designer frontend functions must be attached, so a render of the designer to the designer's view should be in `Index.cshtml` in *Designer* directory. The following actions are required: * Include the links to the files in the folder with static content (the Jquery library is already in the project folder 'template'). * Add the `
` section where the designer will be placed. * Add also a `
``` 4.9 The schema management functions or control functions should be inserted as well, so the next steps are recommended: * Above the `
` section which was placed the Designer object in `Index.cshtml` in the *Designer* directory, must be added a `
` section where buttons that are tied to functions, will be drawn in `workflowdesigner.min.js`. ``` ...
... ``` * Next, add a function to each button in `Index.cshtml` also. The names of the functions must indicate what they do. ``` ``` * Then, styles can be set to make the buttons look pretty in `site.css` in the directory where frontend artifacts are located in -> */wwwroot/css/*. Probably, you will need to edit the styles of the application and the Designer size. ``` body { margin-bottom: 60px; } a { outline: none; text-decoration: none; } .ui { padding: 8px 15px; } .ui.primary.button, .ui.primary.button:focus { background: #f2f2f2; border: 1px solid #f2f2f2; border-radius: 2px; font-weight: normal; color: #2c2c2c; } .ui.primary.button:hover { background: #c9c9c9; } .ui.primary.button:active { background: #b5b5b5; } .ui.secondary.button, .ui.secondary.button:focus { background: #FFFFFF; border: 1px solid #f2f2f2; border-radius: 2px; font-weight: normal; color: #4d4d4d; } .ui.secondary.button:hover { background: #dbdbdb; color: #2c2c2c; } .ui.secondary.button:active { background: #dbdbdb; color: #2c2c2c; } ``` ![Connecting Designer](/documentation/assets/images/styles-site-b9188de9a8461ef6162e22b49e46a4d7.png) 4.10. Finally, run the project. Click on the right-mouse-button in the created application and go to preferred browser. ![Run Project](/documentation/assets/images/wfe-integrate-2-317176fcaaa8ea3c8ff2d662b3dd10d8.png) Afterward, the Designer will be available. ![Designer](/documentation/assets/images/wfe-integrate-3-dc4b142b58623ce9cdad8ccc8de46f93.png) More information related to how to build and connect *Workflow Engine Designer* can be read [here](https://workflowengine.io/documentation/main-terms/designer#frontend). You can see also: * [WorkflowEngine Designer for JavaScript Sample](https://github.com/optimajet/workflow-designer-javascript-sample) * [WorkflowEngine Designer for React Sample](https://github.com/optimajet/workflow-designer-react-sample) * [WorkflowEngine Designer for Angular Sample](https://github.com/optimajet/workflow-designer-angular-sample) In case of any issue, check for errors on the page and refer to [FAQ](https://workflowengine.io/documentation/faq/workflow-engine). You can learn more about Designer in [documentation](https://workflowengine.io/documentation/main-terms/designer). caution If you see some strange designer appearance, for example a modal windows or check boxes are looking strange, it probably means a 'css' conflict. Feel free to send us a message thought the contact form or at , we will do our best to help you. ## 5. Creating a workflow scheme[​](#scheme "Direct link to 5. Creating a workflow scheme") [YouTube video player](https://www.youtube.com/embed/8T_SVgrqZWo) 5.1. Create two commands: 'go' and 'back'. ![Commands](/documentation/assets/images/wfe-integrate-4-b7d5bda2777fb3177baa8e4538676a12.png) 5.2. Create four activities: 'Start', 'State1', 'State2' and 'End'. Set the Initial flag for the Start activity and the Final flag for the End activity. ![Activities](/documentation/assets/images/wfe-integrate-5-5079f124ed8a174c06d692adbba89849.png) 5.3. Create transitions between the activities. For each transition set the *Trigger* (in this example is set *Command* as trigger type) and *Classifier* parameter (it can be: *Not Specified*, *Direct* or *Reverse*). ![Transitions](/documentation/assets/images/wfe-integrate-6-2d0fc507ee9ad71acb19ea3f39e5d382.png) 5.4. Click on 'Save Scheme' button. You can learn more about Schemes in [documentation](https://workflowengine.io/documentation/scheme). ![FinalScheme](/documentation/assets/images/wfe-integrate-7-27eb933e7da934ea7b52d7c7c3755546.png) The final scheme can be downloaded [here](https://workflowengine.io/documentation/assets/files/SimpleWF-2c806d6a648967d88ee1cad697702c96.xml). ## 6. Creating a process and calling commands[​](#commands "Direct link to 6. Creating a process and calling commands") Remember Starting with WFE 7.0, the artifacts for the .NET Framework are no longer available. The .NET Framework 4.6.2 is now the minimum supported version (netstandard2.0), except for Oracle Provider, for which the minimum version is .NET Core 3.0 (netstandard2.1). 6.1. Create a Console Application project. 6.2. [Add](https://docs.microsoft.com/en-us/nuget/tools/package-manager-ui#finding-and-installing-a-package) the following NuGet packages to the project (.NET Framework / .NET Core): * [WorkflowEngine.NETCore-Core](https://www.nuget.org/packages/WorkflowEngine.NETCore-Core/) * [WorkflowEngine.NETCore-ProviderForMSSQL](https://www.nuget.org/packages/WorkflowEngine.NETCore-ProviderForMSSQL/) * [Microsoft.Extensions.Configuration](https://www.nuget.org/packages/Microsoft.Extensions.Configuration/) * [Microsoft.Extensions.Configuration.FileExtensions](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.FileExtensions/) * [Microsoft.Extensions.Configuration.Json](https://www.nuget.org/packages/Microsoft.Extensions.Configuration.Json/) 6.3. Create and add a Reference to the project: [WorkflowLib from step 3](https://workflowengine.io/documentation/how-to-integrate#runtime). 6.4. Add a database connection string. * For .NET Framework: [Add a database connection string](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/connection-strings-and-configuration-files) to the connectionStrings section of the `app.config` configuration file: ``` ``` * For .NET Core: [Add a database connection string](https://blog.bitscry.com/2017/05/30/appsettings-json-in-net-core-console-app/) to the ConnectionStrings section in the `appsettings.json` configuration file: ``` { "ConnectionStrings": { "DefaultConnection": "Data Source=(local);Initial Catalog=wfe_sample;User ID=sa;Password=1" } } ``` Be aware Make sure that server address, authentication type, login and password match your settings. 6.5. Read documentation in the section [how to manage workflows](https://workflowengine.io/documentation/main-terms/basic-operations). 6.6. Add the following namespaces to the `Program.cs` file: info The following lines of code can be different and usually depends on the class library name created in step 2. For example, you have to write "using ClassLibrary;" instead of "using WorkflowLib;" if you defined the library as ClassLibrary. ``` using OptimaJet.Workflow.Core.Runtime; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using WorkflowLib; ``` Add the following *usings* for .NET Core: ``` using System.IO; using Microsoft.Extensions.Configuration; ``` Here's a sample code that shows you how to call basic operations: creating a process, getting a list of available commands, executing commands, setting a state and deleting a process: ``` class Program { static string schemeCode = "SimpleWF"; static Guid? processId = null; static async Task Main(string[] args) { // ------------------------------------------------------ // TODO: This code for .NET Core only (!). // Comment it if you're working with .NET Framework // ------------------------------------------------------ var builder = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true); IConfigurationRoot configuration = builder.Build(); WorkflowInit.ConnectionString = configuration[$"ConnectionStrings:DefaultConnection"]; // ------------------------------------------------------ Console.WriteLine("Operation:"); Console.WriteLine("0 - CreateInstance"); Console.WriteLine("1 - GetAvailableCommands"); Console.WriteLine("2 - ExecuteCommand"); Console.WriteLine("3 - GetAvailableState"); Console.WriteLine("4 - SetState"); Console.WriteLine("5 - DeleteProcess"); Console.WriteLine("9 - Exit"); Console.WriteLine("The process isn't created."); await CreateInstanceAsync(); do { if (processId.HasValue) { Console.WriteLine("ProcessId = '{0}'. CurrentState: {1}, CurrentActivity: {2}", processId, await WorkflowInit.Runtime.GetCurrentStateNameAsync(processId.Value), await WorkflowInit.Runtime.GetCurrentActivityNameAsync(processId.Value)); } Console.Write("Enter code of operation:"); char operation = Console.ReadLine().FirstOrDefault(); switch (operation) { case '0': await CreateInstanceAsync(); break; case '1': await GetAvailableCommandsAsync(); break; case '2': await ExecuteCommandAsync(); break; case '3': await GetAvailableStateAsync(); break; case '4': await SetStateAsync(); break; case '5': await DeleteProcessAsync(); break; case '9': return; default: Console.WriteLine("Unknown code. Please, repeat."); break; } Console.WriteLine(); } while (true); } private static async Task CreateInstanceAsync() { processId = Guid.NewGuid(); try { await WorkflowInit.Runtime.CreateInstanceAsync(schemeCode, processId.Value); Console.WriteLine("CreateInstance - OK.", processId); } catch (Exception ex) { Console.WriteLine("CreateInstance - Exception: {0}", ex.Message); processId = null; } } private static async Task GetAvailableCommandsAsync() { if (processId == null) { Console.WriteLine("The process isn't created. Please, create process instance."); return; } var commands = await WorkflowInit.Runtime.GetAvailableCommandsAsync(processId.Value, string.Empty); Console.WriteLine("Available commands:"); if (commands.Count() == 0) { Console.WriteLine("Not found!"); } else { foreach (var command in commands) { Console.WriteLine("- {0} (LocalizedName:{1}, Classifier:{2})", command.CommandName, command.LocalizedName, command.Classifier); } } } private static async Task ExecuteCommandAsync() { if (processId == null) { Console.WriteLine("The process isn't created. Please, create process instance."); return; } WorkflowCommand command = null; do { await GetAvailableCommandsAsync(); Console.Write("Enter command:"); var commandName = Console.ReadLine().ToLower().Trim(); if (commandName == string.Empty) return; command = (await WorkflowInit.Runtime .GetAvailableCommandsAsync(processId.Value, string.Empty)) .Where(c => c.CommandName.Trim().ToLower() == commandName).FirstOrDefault(); if (command == null) Console.WriteLine("The command isn't found."); } while (command == null); await WorkflowInit.Runtime.ExecuteCommandAsync(command, string.Empty, string.Empty); Console.WriteLine("ExecuteCommand - OK.", processId); } private static async Task GetAvailableStateAsync() { if (processId == null) { Console.WriteLine("The process isn't created. Please, create process instance."); return; } var states = await WorkflowInit.Runtime .GetAvailableStateToSetAsync(processId.Value, Thread.CurrentThread.CurrentCulture); Console.WriteLine("Available state to set:"); if (states.Count() == 0) { Console.WriteLine("Not found!"); } else { foreach (var state in states) { Console.WriteLine("- {0}", state.Name); } } } private static async Task SetStateAsync() { if (processId == null) { Console.WriteLine("The process isn't created. Please, create process instance."); return; } string stateName = string.Empty; WorkflowState state; do { await GetAvailableStateAsync(); Console.Write("Enter state:"); stateName = Console.ReadLine().ToLower().Trim(); if (stateName == string.Empty) return; state = (await WorkflowInit.Runtime .GetAvailableStateToSetAsync(processId.Value, Thread.CurrentThread.CurrentCulture)) .Where(c => c.Name.Trim().ToLower() == stateName).FirstOrDefault(); if (state == null) Console.WriteLine("The state isn't found."); else break; } while (true); if (state != null) { var setStateParams = new SetStateParams(processId.Value, state.Name); await WorkflowInit.Runtime.SetStateAsync(setStateParams); Console.WriteLine("SetState - OK.", processId); } } private static async Task DeleteProcessAsync() { if (processId == null) { Console.WriteLine("The process isn't created. Please, create process instance."); return; } await WorkflowInit.Runtime.DeleteInstanceAsync(processId.Value); Console.WriteLine("DeleteProcess - OK.", processId); processId = null; } } ``` 6.7. Click on right-mouse-button to Run or Debug the Console Application project. ![Run Project](/documentation/assets/images/wfe-integrate-8-7e7c5f463165b1c05555eac3f4d02332.png) Then, the Console Application where you can create processes and execute commands should start. ![Console Application](/documentation/assets/images/wfe-integrate-9-ca308a57b17a8b96830fdf2a01bdff34.png) info Learn more about basic operations [here](https://workflowengine.io/documentation/main-terms/basic-operations). ## Conclusion[​](#conclusion "Direct link to Conclusion") Workflow Engine is one of the easiest workflow engines for document approval when integrating is required. We recommend it to companies that develop information systems with workflow functionality. In addition, you can download our samples [here](https://workflowengine.io/downloads/net-core/). If you have any question, please, do not hesitate to [contact us](https://workflowengine.io/contacts/). --- # How to test workflow schemes ### Why testing is necessary[​](#why-testing-is-necessary "Direct link to Why testing is necessary") Automated testing simplifies the development of business logic because the behavior of workflow schemes can be verified simply by running tests. Reliable tests ensure that scheme changes doesn't mess up existing logic. In addition to reducing development time, this lowers the chance of bugs. ### What's in the guide[​](#whats-in-the-guide "Direct link to What's in the guide") Following this guide, you'll write automated tests for the **Vacation request** workflow scheme from the Workflow Engine [Demo](https://demo.workflowengine.io/designer) (you can learn more about the Demo in [this article](https://workflowengine.io/documentation/demo-description)). To do this, we'll create a separate project in the sample solution and use the following testing tools: * [MSTest](https://learn.microsoft.com/en-us/dotnet/core/testing/unit-testing-with-mstest) framework will run and process the tests. * [Testcontainers](https://www.nuget.org/packages/Testcontainers) package will create an isolated container for the database. * Mockups will make it easier to configure tests. After writing the testing architecture, we'll write three data-driven tests. ### Required software[​](#required-software "Direct link to Required software") * [Docker](https://www.docker.com/). * [.NET 6 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/6.0). * [JetBrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio](https://visualstudio.microsoft.com/). * Terminal. GitHub You can find the code from this guide in the [GitHub](https://github.com/optimajet/workflow-schemes-testing) repository. ## Preparing the environment[​](#preparing-the-environment "Direct link to Preparing the environment") As a tested project, we will download a demo from the public access, set up it's launch and prepare the environment for writing a test project. You can learn more about [Workflow Engine integration](https://workflowengine.io/documentation/how-to-integrate). ### Sample setup[​](#sample-setup "Direct link to Sample setup") Clone our public [GitHub](https://github.com/optimajet/WorkflowEngine.NET/) repository. Open the solution `Samples/ASP.NET Core/MSSQL/WF.Sample.sln` using the IDE. You will see the following solution structure: ![Solution structure](/documentation/assets/images/SolutionStructure-1d797a33aa687e15ce5aff9d1fa3a3b1.png) IDE To work with a .NET project, you'll need an IDE. In this guide, we're working in [JetBrains Rider](https://www.jetbrains.com/rider/), the more popular option is [Visual Studio](https://visualstudio.microsoft.com/). ### Database deploying[​](#database-deploying "Direct link to Database deploying") We'll deploy the database in a docker container. We use the Azure Sql Edge image as the easiest option. Open the console and run the following commands: ``` docker pull mcr.microsoft.com/azure-sql-edge:latest docker run --cap-add SYS_PTRACE -e 'ACCEPT_EULA=1' -e 'MSSQL_SA_PASSWORD=StrongPassword#1' -p 1433:1433 --name azuresqledge -d mcr.microsoft.com/azure-sql-edge ``` * `master` default database. * `sa` database login. * `-e 'MSSQL_SA_PASSWORD=StrongPassword#1'` database password. * `-p 1433:1433` hosting port. * `--name azuresqledge` container name. Docker To run these commands, you need to install and run [docker](https://www.docker.com/). Now you can connect to the database using this connection string: ``` Server=localhost,1433;Database=master;User Id=sa;Password=StrongPassword#1; ``` ### Build and run[​](#build-and-run "Direct link to Build and run") Open the solution. In the `appsettings.json` file, change the connection string to a new one. ``` { "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "DefaultConnection": "Server=localhost,1433;Database=master;User Id=sa;Password=StrongPassword#1;" } } ``` Build and run the project using the following commands: ``` dotnet build WF.Sample.sln dotnet run --project WF.Sample/WF.Sample.csproj ``` Make sure everything works. You can find the scheme whose behavior we'll be evaluating in the Designer menu. If everything is ok, proceed to the next step. ![Workflow scheme](/documentation/assets/images/workflow-scheme-8a707243f3bdfe22478eca0f651b8562.png "Workflow scheme") ## Testcontainers[​](#testcontainers "Direct link to Testcontainers") First of all, we'll work with the Testcontainer package, which will be used to launch the testing database. We'll write several classes to manage and configure the container. ### Project creating[​](#project-creating "Direct link to Project creating") Create a new project according to the "Unit test project" template. ``` dotnet new mstest --name WF.Sample.Tests dotnet sln add WF.Sample.Tests ``` Remove the example classes and divide the project into logical parts by creating directories: * Mockups * Testcontainer * Tests * configs And create a static `TestData` class that will store information common to all tests. ![Project structure](data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQgAAACNCAYAAABL/OQgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAABMWSURBVHhe7d19cFVlfgfw7yUCQrsLiPxhlUDIJWs7dVbH7pBgQtpghMSOuKWQoiaDmMWatLvakp11m4WEZuqMZGctbZguYqCJQgPbUma2CS/KTEiGhK4udnW6JSaERB1aEXnpFNxFSM/vOefce3LveW7uW+K9Od/PzJl77nnjXsfzu8/znHO+8X3++ecjICJyMcV6TVu3bt3CF198oSaZJ6LkSdkWhJzwY7lx4waam5tx+PBh9X7lypV45plncPvtt6v3RJSYlC4Qy5Yts96F27x5MwYGBtA/cBb3V9SoZe+2bIM/exGee+45TJmS9o0joi9d2p5F999/P44cOaKKw9YzU9Qk87KMXQ2i5BiXAnH16lVcu3bNejeaLJf1E6mrfgXqu4INpZGRHtQvqULLkGNZ18tYUt9jrVuB3NyVgam85UNrqyDZ3l6/ZMnL6BoZ/4aY2+eORL6383vYk9v3IXIzLgVCfsX37t0bViTkvSyX9dGSbd2mOXPmYMWKFapbsflrt9Qk87IstHuRX7Qc7cd7rXeG4WH0YwDHOj+yFhiLBs8hJ+se6102qto60Nt7WE2tFfOt5aaRoTZUbAK29Vjb7F+AwW5rZQop2HJEfb6eni0oGVkU+E6h3yeSWIsSTS7jUiAeffRR9eosEnZxEPb6RMgA5YYNG9SYw09+8C01ybwMUoaNP2QuQE7/MIasX/nhzk74qzcAg2aBGBn5EJ3HgOJCu0CMYXgIfTkLkGm99S0oQ0WBz3pHNHmMS4GYOXMmnnjiCTUvReHChQuB4iDLZX2sfD4fpk6dqq5QzJgxA9OnT1eFYOPGjTh48KCann32Wdx2223hYxCZS1GMTnQOB4tBVmEm/O1dVtfgIwz2LUSWfcaPJb8ApX3NqG0NtkBszma9dFlE4Fe4qw3lVvdFujxDLVVhzX63bXVdAmnJ2NvE2s1x21f+27SUBz9/3YmT2Jpbhw7fWewoK7G6YKO3cXbdaPIZlwIhnEXitddeU68JFYdpU7H39Osof2MdHt7x+3iytQynhnvVuppDf4F3Pz4deJVlTj7ffGT5BzBoFAhVDFCIwsxcFJWeM5d1d6G9tAAFgf0G1AmhOwl8vjxs7t2F4mOVYeudzfrS9lZH03wATTuBBlm3rQgdNSWoxYvmtsb7vqZ9jhPcsW3b00DTS2FNfCkkW9d2oni/2Q3qbQR2uhQsN9p9u/ehyV9nLjOmumVLje9ZF+ienNqSF7bNFracJrVxKxDCLhL33ntv3MVBSKtg789fR8vP9iB7rh8NpX+DNQ+U4Vc3f6XWX7p+CbNnzA683rx5Uy13CoxDSDHwZ2KBUQwys6DGIUaPP4jRYxBuJ4EUnYrWI+oE7t8ULBL24GVeXj3a1RJbNqob1qp/V1ogctIFujTSIoFVrJTgttJ92VhqFzcHaxzFLmR5NcfRZ3WZxqTbV7pi7XWRBzGj2YYmjXEtEEKKwuOPPx53cRAZGRl488xR3D3rbmxZuRW5C5fim/etxh/4i/Dn/1yFc58NovKfnlav8t6VNQ7RbRSD0qJcc1FhITB4MrbxhxByAjdUZ6vi4xy87Ol5FdU51kYJkCb9YL/1Jszy4ECpMalf+KiF7yvfpfXUETTgJdeWk4hmG5o8xr1AJMvFaxcxf3YmpvhGf+S//aO/x9SMqTj8p2+q10OV/2atCaHGIZqxqQnBsYZMGYdoRlMU4w/O0fyRrrZAk98c0xgwWyDOwcvhkzjWpzaJg+MKizrOchTlm28D5LPjrbBuhfNzamn2tS2o2IG2qkXoj9AiiWYbSn8pXSBOnDihBiRlTGHuzLkYuNiPWyOjByBX7XoUN27ewMp/eFi9fudf/ixsDEJIl6CwOBvIkfEHe1keikqNmVHjD1HIz8TgWnN8Ii/vW6pPri4d5q9DtVGEyvKMpnvtEPxxtyCy4R80f6HzynbD31gT9vnUOMj+DUCTOQ4ikz0oOhbdvs57O9Y2LcTG8nus/0Zmd0S3DU1eafE0p4wpyPjDP/77bjyUlY+S3y7F+f89j9kz5iDrjiw0HN2K2kc2q9ddf7Lb+J/ap7ol6UgNIOa2Imt/EyoWxFC0iMZBWnQx5GR/8sFyVHxjvWpF1LZ/HwdOt2F6xnRcvn4Zc4xCYb+mc3EgSjVplQdhX50YsS4HSjGw521yb0Q6P6jFFgSlEgbGEJFW+v7UEtG4Y4EgIi0WCCLSYoEgIi3PFgiG3RKNzZNXMa5fv67Cbjs6OtT7kpISlS0hd20SUVDKtyCuXLmCAwcOhKVTxUtaC/L4+ZkzZ7B9+3Y1ybwsY0uCaLSULxASEiNFwplOlQgpAhJZ9/zzz2PevHlqknlZNlaBMB+ECoalyJQKjz1H9YBWHMxQmeQfl9JHyhcIO1NCJKtIJCaYFSGhMP6myqgfkko35qPdO3hHp4elxSBlMouE3IYtwbavvPKKisKTSebdwm7HYj8VmROIriOaXNKiQAgpEqtWrcKnn36Ko0ePWktjJ0WgsrISs2fPxlNPPaUmmZdlcT3DITkTOW/huJVq7Z71GDlnMuI+LcHHq3X7VLSOjpuK63guWZOhXRf34zKjcjJLmwIhrYZDhw7hzjvvxCOPPGItjZ088PX+++/j7bffDgxSyrwsc4uqi4WcUPqcSPecyTH3GSwwuzOO3Epzn2b4G819GtAZiLeL73gforWiEseKd5n7GFNozJ72uMyonNTSokBIcUgkFVsGH+17Hk6fPq3+bN/WrVvxwAMPqEnmZZmsi+/eiGwzkSpiTqQmZ3Ksfdab8Xijcitln5wNWG+lTGWWlxvrLHEdz0ytihj+kkiOJaWtlC8QySgOJ0+exOrVq/HYY4/hxRdfDBQHyY2QyS4Ssk62kW1ln6iKhDq5nJF1Y+dEhudMJpIt6SbZx7PFl2NJ6SvlC4T8gZxZs2bFnYotJ/m2bdvwySef4NKlSyohW/6upzNURuZlmayTbWRb2WesAhFs6luRcBGzHjU5k2PkQ7qSffqascca9xhubQ0maMd1PHMcJeI+ScixpPST8gVCisOaNWviKg5upk2bZs2Fi7QuKNjMzlXBLsF+d+ScSPecyXiyJdU+jcvV39aQ7WtRGOhixHe8+UYXoU5dsrX3CW0J6I7LjMrJbdLfam13MaRFIGpqarB06dKwKxbRbhcP1dJgShSlIU88iyEnv91dkBNed9JHu12sWCAoXaV8FyMZ5ESX8QWZIp300W5H5BXMpCQiLf5MEpEWCwQRabFAEJEWCwQRaXGQ0iAPaYX+hS6bXNEg8irPFwgpDv/duR2fnPg7a0nQfbV9LBDkaZ7oYthPaLpNuuJARB5pQUgheK8hx3oXvYloQcizDPLo9Mjip9H21yP4QdkJFO/nHZeUGlggIhjvAmHegl0HNDJohVKTpwqE/y//w1oytv4ffn2CCgSf0aDUxcucMdJlMDrzGkctN4qAWxak3Xro8J1Vj4/by3QZkOUtbYF1zIGkicICEQNddqM62R0ZkZI52b+p2vH3JMKzILuRi829dSgZWaRi9Fsr5lvbmkKP6cydZA4kTRQWiFjoshtVXqOVEGUYlTmpaLIgI4mUO8kcSJogLBBpiDmQNFFYIGKhy2608hqdfxtjZ3uwRRGXSLmTFuZA0nhjgYiBLrvRzmvs32Quy1V/P8IKso1TpNxJ5kDSROF9EBGk0q3W0iqpWDuEjb2JFR6iWHimQMQrVQpEV/0KbOrfgP0t5h/fIZoInigQ6ci+pLrjA7MYjIwUoZGtB5pgLBBEpMVBSiLSYoEgIi0WCCLSYoEgIi0OUhqYSUnkzvMFgpmURHqe6GLIjVK6iZmURHqeuZMyGbda2yEv7aV1OLUlz1oaNNRShbVNQPX++BKizOMzYYpSBwcpY5aNnP5WRxiMSU7uPU0D1juiycFTBUIyKaOdIvH7gWOdIY9Yd3cZLYvlwVAXokmALYg4ZK0vh79pH7qsKx/quYmd51C9vkC9t+lyKsVYuZLSIqk39l1S32PNO7IqHe8D813O/EozaYrZlZQoFoi45KKoNBgQo6LoUIjCTOu9QU5cXU6lLtsy6CNjvRmH7zbWEW4ATTuBBuvfQdNLZjFhdiUliAUiTvnrN6B/534MGa2I7j1GIdi4Fo76EDmnUpdtaTlWaxaP6E/obFQ3mI+Bj/p3mF1JCWKBiJfEz6ETnd37sbM/GC6bOBnozEZfnDFy0joZ7DfnmV1JiWKBiJOKn9u4EDtqdgPFS8NDXCLlVOqyLZVsFDc0oRHOX/57kJXjSMmWAVFr1jQQHDS1WifOPExmV1K8WCASkb8OVYuLXLsKkXIqddmWTvmbd6H4WCWWlLdh2CgQ5RuD+ZS5xxFytSQb/kGzlZBXthv+RvPfYXYlJYo3SkWQDrdaq8FQ3lxF48QzBSJeLBDkZZ7oYshJHu9E5GV83JuItDhISURaLBBEpMUCQURaLBBEpMVBSgMzKYnceb5AMJOSSM8TXQw7f9JtYiYlkR5vtY4gtAUhf2G7piP8bsXFVa+itWK+9S49yMNjFer5EN6BSXosEBHouhiJ3N483rdG89ZrSiZPXcVwy57UTUTEy5xJ48yfXLLkZZVXGZoJWXfipPHrXocO31nsKCtReZNqX012pC7TUloJKoeyJfg4t2RHmK2H8ONL18jeLvhvuuRahhzP5v7dzH3q66sCy2jyYYFIAnViqv68mf3Y2wgzDCYkE7Ju2VJs7q1DycgiVLV1qLxJXT6leUz3TEvTAJoGC8x124rQ17QP3cgNO74o2HLE3K5nC0rbwyP7TeHHswuB63ez9unPehGnTn1X5U/Q5MMCkQwqf3JA/WrLr2xezXEzMi6aTEhdPmWkTEslG9Xrc83Z/AKU4pxj3Wh2cExeXn1IEpWT5ni676Zko7iQITSTGQtE0izHtp4O81fWmOTXOxUyIdXVik1Qn62n51VUxz5Wawj/buQNLBDJYOVPumdMjpEJqcunjJRpGYvhIfTlLDATt1VrRS2N3hjfjSY3FogksPMn4ciYVH/wxiUTUrYtMroK9iCiLp8yUqZlJKHHl9zMajSjLM/oItQOwR9jC0L33cgbeB9EBLzVmrzOMwUiXiwQ5GWef1iLiPQ4BkFEWiwQRKTFAkFEWiwQRKTFAkFEWryKYWAmJZE7zxcIZlIS6XmiiyE3SukmZlIS6fFW6wgmcyYlUTRYICJIx0xKomTy1FUMt+xJ3UREvMyZNPFmUuryKIlSAQtEEiSSSRm6jeRREqUKFohkSCSTMpptiL4kLBBJE18mZSrkVhLpsEAkQyKZlJZotiGaaCwQSZBIJqXbNkSpgvdBRMBbrcnrPFMg4sUCQV7m+Ye1iEiPYxBEpMUCQURaLBBEpMUCQURaLBBEpMUCQURaLBBEpOWp+yAkufrWrVvqVfh8PkyZYtZICa8NlZGRobYh8irPFAgpAP/1P7/E3ndex7sfnzaW+LDwjoX4TuELyL7TjzfebsV753+B7z38V1jdvAp7y9vwW3PuNne2jEcmpRlBJyEyweMy45JShSe6GNJq6L/wAV7412+j59xJPLSoAKu//seY4ptilAmfalFcvn4Zs26fhSvGq/iqMS/7ORVsOaIe5e7p2RIIfZH3sZzMUhDql1ShZchZl7MDx5Jj+5sq1YNckbgfhyi5PFEgpAC88U4rfv3Fr/HEg0/hu0XfQ8U31uNH39yOrLmL8POP3kHfhTP47NpnOP7BW8iYkoGus51hBWIi2E+G5rR3qdg6oi+TZwrE++ffU/PLcx5Wr069Rqti8OIgzl89j66BTkzNmIpO4zWW8Yd4MyldZS5Fcc5bON5tvpWujX0MM8fS7paMPk7odm7cMjCZi0k6nnmas+THxbhx8wYOVf4UvzHtN601QVUHnkXp7/yhmm//z59ix5ofqwIhA5WhzBM0GF0f9r7rZVQMrkNL1j7kHi8wsyctY+1rbvOh0W2pxODG0RmVzm3LM3vD9rO5HVPYxz1WvGtUt0hlUoR8TiLhmcucX5n+FfX66f9dVK9Ote3fx/DlIRz8xU/QdnofPr7yMXZ0x/DXthLJpNTKRlamOWeHyuTl1aPdXORqzO2GT+JY3/LwUBrmYpKGJwqEtAR+96771PyRX4afOoXZhap1UWC83vXVu5A1Nwu5C5daa6MVXyalK3UiL1QFQrouFZugjt3T8yqqNbk30W7nhrmYpOOZAvHkg+WYdts07H+3DS+92YCWn+3BCwe/rVoLBYsKcfPWTRQtXo47Zt6BnHlfw4Pzfy9wj8SYkpBJaVPdg7XN8DfWoEDGQIaH0JezAKoxoQqH2iycZjs5XuBqhzW2kYzPSd7giQIhJ7p/3mL86PHteCgrH73nenDovYOYPycTs2fMxtXPr6jtZhnzV4x5WSZFJdpBykQyKU3B7kmuGjtwjD3kr0M1mlGWZ6yvHYLfahmEHUeznZPPN9/oRtSpy6j251KDlC6fk0h4/k5KmdwuZ/IuSiKPFQgiio1nrmIQUexYIIhIiwWCiLRYIIhIiwWCiDSA/weQn90IPXr7IgAAAABJRU5ErkJggg== "Project structure") ### Testcontainer configuration[​](#testcontainer-configuration "Direct link to Testcontainer configuration") Install package [Testcontainers](https://www.nuget.org/packages/Testcontainers) via NuGet. This library allows you to programmatically run database images in docker containers. This will be useful in order to automatically initialize a clean database each time the tests are run. ``` dotnet add WF.Sample.Tests package Testcontainers ``` In the `./Testcontainer/` directory, create an `AzureConfiguration` class inherited from `TestcontainerDatabaseConfiguration`. It will save the configuration of the Testcontainer. ``` using DotNet.Testcontainers.Builders; using DotNet.Testcontainers.Configurations; namespace WF.Sample.Tests.Testcontainers; public class AzureConfiguration : TestcontainerDatabaseConfiguration { private const string AzureSqlEdgeImage = "mcr.microsoft.com/azure-sql-edge"; private const int AzureSqlEdgePort = 1433; public AzureConfiguration() : this(AzureSqlEdgeImage) { } public AzureConfiguration(string image) : base(image, AzureSqlEdgePort) { Environments["ACCEPT_EULA"] = "Y"; OutputConsumer = Consume.RedirectStdoutAndStderrToStream(new MemoryStream(), new MemoryStream()); } public override string Database { get => "master"; set => throw new NotImplementedException(); } public override string Username { get => "sa"; set => throw new NotImplementedException(); } public override string Password { get => Environments["SA_PASSWORD"]; set => Environments["SA_PASSWORD"] = value; } public override IOutputConsumer OutputConsumer { get; } public override IWaitForContainerOS WaitStrategy => Wait.ForUnixContainer() .UntilPortIsAvailable(AzureSqlEdgePort) .UntilMessageIsLogged(OutputConsumer.Stdout, "SQL Server is now ready for client connections"); } ``` Create an `AzureDatabase` class that inherits from `TestcontainerDatabase`. It will override the way the connection string is created. ``` using DotNet.Testcontainers.Configurations; using DotNet.Testcontainers.Containers; using Microsoft.Extensions.Logging; namespace WF.Sample.Tests.Testcontainers; public class AzureDatabase : TestcontainerDatabase { internal AzureDatabase(ITestcontainersConfiguration configuration, ILogger logger) : base(configuration, logger) { } public override string ConnectionString => $"Server={Hostname},{Port};Database={Database};" + $"User Id={Username};Password={Password};" + $"TrustServerCertificate=True"; } ``` To read options from the configuration file, describe them in the `DbOptions` class. ``` namespace WF.Sample.Tests.Testcontainers; public class DbOptions { public DbOptions(string name) { Name = name; } public string Name { get; } public int? Port { get; set; } public string? Password { get; set; } } ``` ### Migrations[​](#migrations "Direct link to Migrations") Create `MigrationExtensions` class which will contain the path to the scripts. ``` namespace WF.Sample.Business.Migrations { public static class MigrationExtensions { public static string GetEmbeddedPath(string fileName) { return $"WF.Sample.Business.Scripts.{fileName}"; } } } ``` Then, we will create two migration scripts. Please note that, due to the differences in the various databases, the actual implementation may vary. You can find an example implementation for another database in the samples. ``` using FluentMigrator; namespace WF.Sample.Business.Migrations { [Migration(2000010)] public class Migration2000010CreateObjects : Migration { public override void Up() { Execute.EmbeddedScript(MigrationExtensions.GetEmbeddedPath("CreateObjects.sql")); } public override void Down() { } } } ``` ``` using FluentMigrator; namespace WF.Sample.Business.Migrations { [Migration(2000020)] public class Migration2000020FillData : Migration { public override void Up() { Execute.EmbeddedScript(MigrationExtensions.GetEmbeddedPath("FillData.sql")); } public override void Down() { } } } ``` Finally, you need to place the scripts for the `Createobject.sql` and `FillData.sql` migrations in the `./Scripts` directory and add them as project resources. ``` ``` ### Testcontainer runtime[​](#testcontainer-runtime "Direct link to Testcontainer runtime") Create the `DbRuntime` class to run and control the Testcontainer. ``` namespace WF.Sample.Tests.Testcontainers; public class DbRuntime { public DbRuntime(DbOptions options) { _options = options; } public string Name => _options.Name; public async Task StartAsync() { throw new NotImplementedException(); } public async Task StopAsync() { throw new NotImplementedException(); } private readonly DbOptions _options; private AzureDatabase? _dbTestContainer; } ``` To set the container settings, create an extension for the inheritors of the `TestcontainerDatabaseConfiguration` class. ``` using DotNet.Testcontainers.Configurations; namespace WF.Sample.Tests.Testcontainers; public static class Extensions { public static T WithSettings(this T configuration, DbOptions options) where T : TestcontainerDatabaseConfiguration { if (options.Password is not null) configuration.Password = options.Password; if (options.Port is not null) configuration.Port = options.Port.Value; return configuration; } } ``` Implement the `StartAsync` and `StopAsync` methods in the `DbRuntime` class. To work with the database install the [Microsoft.Data.SqlClient](https://www.nuget.org/packages/Microsoft.Data.SqlClient/) package via NuGet. ``` dotnet add WF.Sample.Tests package Microsoft.Data.SqlClient ``` ``` using DotNet.Testcontainers.Builders; using Microsoft.Data.SqlClient; using OptimaJet.Workflow.Core.Runtime; using OptimaJet.Workflow.DbPersistence; using OptimaJet.Workflow.Migrator; using WF.Sample.Business.Migrations; namespace WF.Sample.Tests.Testcontainers; public class DbRuntime { public DbRuntime(DbOptions options) { _options = options; } public string Name => _options.Name; public string ConnectionString => TestContainer.ConnectionString; public AzureDatabase TestContainer => _dbTestContainer ?? throw new Exception($"The instance of '{nameof(DbRuntime)}' not initialized"); public async Task StartAsync() { _dbTestContainer = new TestcontainersBuilder() .WithDatabase(new AzureConfiguration().WithSettings(_options)) .Build(); await _dbTestContainer.StartAsync(); //This method is needed to make sure the database is up and ready to accept our requests. EnsureDatabaseReady(); InitDatabase(ConnectionString); } public async Task StopAsync() { await TestContainer.DisposeAsync().AsTask(); } private void EnsureDatabaseReady() { for (var i = 0; i < 300; i++) { try { using var connection = new SqlConnection(ConnectionString); connection.Open(); return; } catch (Exception) { Thread.Sleep(100); } } } private static void InitDatabase(string connectionString) { var mssqlProvider = new MSSQLProvider(connectionString); new WorkflowRuntime() .WithPersistenceProvider(mssqlProvider) .RunMigrations() .RunCustomMigration(typeof(Migration2000010CreateObjects).Assembly); } private readonly DbOptions _options; private AzureDatabase? _dbTestContainer; } ``` ### TestData implementation[​](#testdata-implementation "Direct link to TestData implementation") Now we should prepare the configuration files and make a `DbRuntime` factory. The process of creating and controlling `DbRuntime` will be managed by the `TestData` class. This class will act as a hub for all testing singletons. Configure the project .csproj file so that the contents of the `./configs` folder is always copied to the build directory. To do this, add the following settings: ``` ``` Add the `options.json` file in `configs` folder. ``` { "name": "Mssql", "port": 11433, "password": "P@ssw0rd" } ``` Now we'll code reading `DbOptions` in the `TestData` static constructor. In that class, we'll store constants for accessing configuration files. In the `StartAsync` and `StopAsync` methods, all objects used in tests will be created and disposed. Right now it's just `DbRuntime`, but we'll add a few more services later. ``` using Newtonsoft.Json; using WF.Sample.Tests.Testcontainers; namespace WF.Sample.Tests; public static class TestData { static TestData() { var optionsJson = File.ReadAllText(Path.Combine(ConfigPath, OptionsConfigName)); var options = JsonConvert.DeserializeObject(optionsJson) ?? throw new InvalidOperationException("Json deserialization error."); _dbRuntime = new DbRuntime(options); } public static async Task StartAsync() { await _dbRuntime.StartAsync(); } public static async Task StopAsync() { await _dbRuntime.StopAsync(); } public static string ConnectionString => _dbRuntime.ConnectionString; public const string ConfigPath = "./configs"; public const string OptionsConfigName = "options.json"; private static readonly DbRuntime _dbRuntime; } ``` ## Mockups & WorkflowRuntime[​](#mockups--workflowruntime "Direct link to Mockups & WorkflowRuntime") To limit the set of tested elements and simplify the configuration of the tested project, we use mockups. When creating a WorkflowRuntime object, we'll replace some services with simplified classes which we can control on tests running. We'll also need access to the main project, so set up the .csproj file to make the references: ``` dotnet add WF.Sample.Tests reference WF.Sample dotnet add WF.Sample.Tests reference WF.Sample.Business ``` ### DocumentRepository mockup[​](#documentrepository-mockup "Direct link to DocumentRepository mockup") This repository manages `Document` DTOs. We will make a simplified version of this class, that just includes the data required for tests. In `./Mockups/` folder Create class `TestDocument`. The `TestDocument(Document document)` constructor and the `ToDocument` method will map this DTO to the original one. The `TestDocument(Employee author)` constructor will allow us to create new documents in the tests. ``` using WF.Sample.Business.Model; namespace WF.Sample.Tests.Mockups; public class TestDocument { public TestDocument(Employee author) { Author = author; Manager = author; Id = Guid.NewGuid(); Sum = 100; } public TestDocument(Document document) { Id = document.Id; Sum = document.Sum; Author = document.Author; Manager = document.Manager; } public Guid Id { get; set; } public decimal Sum { get; set; } public Employee Author { get; set;} public Employee Manager { get; set; } public Document ToDocument() { return new Document { Id = Id, Name = Id.ToString(), Comment = String.Empty, AuthorId = Author.Id, ManagerId = Manager?.Id, Sum = Sum, State = String.Empty, StateName = String.Empty, Author = Author, Manager = Manager, }; } } ``` Now we'll create a mockup of the document repository service. Create the `TestDocumentRepository` class implement the `IDocumentRepository` interface. It has a "TestDocument" list and update/returns its elements transferred to the "Document" by requests as if they were stored in the database. ``` using OptimaJet.Workflow.Core.Persistence; using WF.Sample.Business.DataAccess; using WF.Sample.Business.Model; namespace WF.Sample.Tests.Mockups; public class TestDocumentRepository : IDocumentRepository { public TestDocumentRepository() { Documents = new List(); } public List Documents { get; } public Document InsertOrUpdate(Document document) { var testDocument = Documents.FirstOrDefault(d => d.Id == document.Id); if (testDocument != null) { testDocument.Author = document.Author; testDocument.Manager = document.Manager; testDocument.Sum = document.Sum; } else { Documents.Add(new TestDocument(document)); } return Documents.First(d => d.Id == document.Id).ToDocument(); } public List Get(out int count, int page = 1, int pageSize = 128) { var paging = new Paging(page, pageSize); var result = Documents.Take(paging.PageSize).Skip(paging.SkipCount()).Select(d => d.ToDocument()).ToList(); count = result.Count; return result; } public Document? Get(Guid id, bool loadChildEntities = true) { return Documents.FirstOrDefault(d => d.Id == id)?.ToDocument(); } //We dont track number in TestDocument so we didn't need to implement this method public Document? GetByNumber(int number) { return null; } public List GetByIds(List ids) { return Documents.Where(d => ids.Contains(d.Id)).Select(d => d.ToDocument()).ToList(); } public void Delete(Guid[] ids) { foreach (var document in Documents) { if (ids.Contains(document.Id)) { Documents.Remove(document); } } } //We dont track state in TestDocument so we didn't need to implement this method public void ChangeState(Guid id, string nextState, string nextStateName) { } //This method is unused so we didn't need to implement it public bool IsAuthorsBoss(Guid documentId, Guid identityId) { throw new NotImplementedException(); } //This method is unused so we didn't need to implement it public IEnumerable GetAuthorsBoss(Guid documentId) { throw new NotImplementedException(); } } ``` ### EmployeeRepository mockup[​](#employeerepository-mockup "Direct link to EmployeeRepository mockup") In the test configuration, we will make a list of the employees along with their roles. Create the `TestEmployeeRepository` class implement the `IEmployeeRepository` interface. It will get a list of employees when it's created and return its elements on requests like in the `TestDocumentRepository`. ``` using OptimaJet.Workflow.Core.Persistence; using WF.Sample.Business.DataAccess; using WF.Sample.Business.Model; namespace WF.Sample.Tests.Mockups; public class TestEmployeeRepository : IEmployeeRepository { public TestEmployeeRepository(List employees) { Employees = employees; } public const string Unknown = "Unknown"; public List Employees { get; } public List GetAll() { return new List(Employees); } public string GetNameById(Guid id) { return Employees.FirstOrDefault(e => e.Id == id)?.Name ?? Unknown; } public IEnumerable GetInRole(string roleName) { return Employees .Where(e => e.EmployeeRoles.Any(r => r.Role.Name == roleName)) .Select(e => e.Id.ToString()); } public bool CheckRole(Guid employeeId, string roleName) { return Employees.Any(e => e.Id == employeeId && e.EmployeeRoles.Any(r => r.Role.Name == roleName)); } public List GetWithPaging(string? userName = null, SortDirection sortDirection = SortDirection.Asc, Paging? paging = null) { var result = new List(Employees); if (userName != null) { result = result.Where(e => e.Name.Contains(userName)).ToList(); } if (sortDirection == SortDirection.Desc) { result = result.OrderByDescending(e => e.Name).ToList(); } else { result = result.OrderBy(e => e.Name).ToList(); } if (paging != null) { result = result.Skip(paging.SkipCount()).Take(paging.PageSize).ToList(); } return result; } } ``` Save predefined employee entries in `./configs/employees.json`. Each role has one employee with the same name. The *Guest* employee has no roles, the *Manager* employee has a user role, but will act as a manager in document. The *SuperUser* has all the roles, so we can exclude roles from testing. ``` [ { "Id": "dc114758-d201-488e-ba88-a4a24bb57a3c", "Name": "SuperUser", "StructDivisionId": "00000000-0000-0000-0000-000000000000", "StructDivision": null, "IsHead": false, "EmployeeRoles": [ { "EmployeeId": "dc114758-d201-488e-ba88-a4a24bb57a3c", "RoleId": "8d378ebe-0666-46b3-b7ab-1a52480fd12a", "Role": { "Id": "8d378ebe-0666-46b3-b7ab-1a52480fd12a", "Name": "Big Boss" } }, { "EmployeeId": "dc114758-d201-488e-ba88-a4a24bb57a3c", "RoleId": "412174c2-0490-4101-a7b3-830de90bcaa0", "Role": { "Id": "412174c2-0490-4101-a7b3-830de90bcaa0", "Name": "Accountant" } }, { "EmployeeId": "dc114758-d201-488e-ba88-a4a24bb57a3c", "RoleId": "71fffb5b-b707-4b3c-951c-c37fdfcc8dfb", "Role": { "Id": "71fffb5b-b707-4b3c-951c-c37fdfcc8dfb", "Name": "User" } } ] }, { "Id": "49c3dc51-462d-488c-ac31-41af8b3a13c0", "Name": "BigBoss", "StructDivisionId": "00000000-0000-0000-0000-000000000000", "StructDivision": null, "IsHead": false, "EmployeeRoles": [ { "EmployeeId": "49c3dc51-462d-488c-ac31-41af8b3a13c0", "RoleId": "8d378ebe-0666-46b3-b7ab-1a52480fd12a", "Role": { "Id": "8d378ebe-0666-46b3-b7ab-1a52480fd12a", "Name": "Big Boss" } } ] }, { "Id": "3640c894-3b83-4d45-ade2-d9c3bd4a77ff", "Name": "Accountant", "StructDivisionId": "00000000-0000-0000-0000-000000000000", "StructDivision": null, "IsHead": false, "EmployeeRoles": [ { "EmployeeId": "3640c894-3b83-4d45-ade2-d9c3bd4a77ff", "RoleId": "412174c2-0490-4101-a7b3-830de90bcaa0", "Role": { "Id": "412174c2-0490-4101-a7b3-830de90bcaa0", "Name": "Accountant" } } ] }, { "Id": "0a825b20-62d5-4045-a5c3-8a5bdb08bd25", "Name": "User", "StructDivisionId": "00000000-0000-0000-0000-000000000000", "StructDivision": null, "IsHead": false, "EmployeeRoles": [ { "EmployeeId": "0a825b20-62d5-4045-a5c3-8a5bdb08bd25", "RoleId": "71fffb5b-b707-4b3c-951c-c37fdfcc8dfb", "Role": { "Id": "71fffb5b-b707-4b3c-951c-c37fdfcc8dfb", "Name": "User" } } ] }, { "Id": "6a3419d7-95a5-4b2b-939c-bd2dba485867", "Name": "Manager", "StructDivisionId": "00000000-0000-0000-0000-000000000000", "StructDivision": null, "IsHead": false, "EmployeeRoles": [ { "EmployeeId": "6a3419d7-95a5-4b2b-939c-bd2dba485867", "RoleId": "71fffb5b-b707-4b3c-951c-c37fdfcc8dfb", "Role": { "Id": "71fffb5b-b707-4b3c-951c-c37fdfcc8dfb", "Name": "User" } } ] }, { "Id": "22893a7f-cef7-43b6-99cb-05517f7d2194", "Name": "Guest", "StructDivisionId": "00000000-0000-0000-0000-000000000000", "StructDivision": null, "IsHead": false, "EmployeeRoles": [] } ] ``` Modify `TestData` class to load and save predefined employees in public List. ``` using Newtonsoft.Json; using WF.Sample.Business.Model; using WF.Sample.Tests.Testcontainers; namespace WF.Sample.Tests; public static class TestData { static TestData() { var employeesJson = File.ReadAllText(Path.Combine(ConfigPath, EmployeesConfigName)); var employees = JsonConvert.DeserializeObject>(employeesJson) ?? throw new InvalidOperationException("Json deserialization error."); Employees = employees; var optionsJson = File.ReadAllText(Path.Combine(ConfigPath, OptionsConfigName)); var options = JsonConvert.DeserializeObject(optionsJson) ?? throw new InvalidOperationException("Json deserialization error."); _dbRuntime = new DbRuntime(options); } public static async Task StartAsync() { await _dbRuntime.StartAsync(); } public static async Task StopAsync() { await _dbRuntime.StopAsync(); } public static string ConnectionString => _dbRuntime.ConnectionString; public static List Employees { get; } public const string ConfigPath = "./configs"; public const string OptionsConfigName = "options.json"; private const string EmployeesConfigName = "employees.json"; private static readonly DbRuntime _dbRuntime; } ``` ### WorkflowRuntime[​](#workflowruntime "Direct link to WorkflowRuntime") We'll create an instance of `WorkflowRuntime` using the `WF.Sample.Business.Workflow.WorkflowInit` class, but as an `IDataServiceProvider` we'll pass a mock implementation into which we'll place services created on previous steps. Create a final mockup service: `TestPersistenceProviderContainer`, which implements the `IPersistenceProviderContainer` interface. ``` using OptimaJet.Workflow.Core.Persistence; using OptimaJet.Workflow.DbPersistence; using WF.Sample.Business.DataAccess; namespace WF.Sample.Tests.Mockups; public class TestPersistenceProviderContainer : IPersistenceProviderContainer { public TestPersistenceProviderContainer() { Provider = new MSSQLProvider(TestData.ConnectionString); } public IWorkflowProvider Provider { get; } } ``` Create a `TestDataServiceProvider` that implements the `IDataServiceProvider`. It will find a suitable type in the switch and return one of our services created earlier. ``` using WF.Sample.Business.DataAccess; namespace WF.Sample.Tests.Mockups; public class TestDataServiceProvider : IDataServiceProvider { T IDataServiceProvider.Get() { return (T) Get(typeof(T)); } private object Get(Type type) { return type.Name switch { nameof(IPersistenceProviderContainer) => new TestPersistenceProviderContainer(), nameof(IEmployeeRepository) => _employeeRepository, nameof(IDocumentRepository) => _documentsRepository, _ => throw new ArgumentOutOfRangeException() }; } private readonly TestEmployeeRepository _employeeRepository = new(TestData.Employees); private readonly TestDocumentRepository _documentsRepository = new(); } ``` And finally, add work with `WorkflowRuntime` to the `TestData` class. ``` using Newtonsoft.Json; using OptimaJet.Workflow.Core.Runtime; using WF.Sample.Business.DataAccess; using WF.Sample.Business.Model; using WF.Sample.Business.Workflow; using WF.Sample.Tests.Mockups; using WF.Sample.Tests.Testcontainers; namespace WF.Sample.Tests; public static class TestData { static TestData() { var employeesJson = File.ReadAllText(Path.Combine(ConfigPath, EmployeesConfigName)); var employees = JsonConvert.DeserializeObject>(employeesJson) ?? throw new InvalidOperationException("Json deserialization error."); Employees = employees; var optionsJson = File.ReadAllText(Path.Combine(ConfigPath, OptionsConfigName)); var options = JsonConvert.DeserializeObject(optionsJson) ?? throw new InvalidOperationException("Json deserialization error."); _dbRuntime = new DbRuntime(options, scripts.ToArray()); } public static async Task StartAsync() { await _dbRuntime.StartAsync(); WorkflowInit.Create(new TestDataServiceProvider()); WorkflowRuntime = WorkflowInit.Runtime; } public static async Task StopAsync() { await _dbRuntime.StopAsync(); } public static WorkflowRuntime WorkflowRuntime { get => _workflowRuntime ?? throw new NullReferenceException("WorkflowRuntime not initialized."); private set => _workflowRuntime = value; } public static IDataServiceProvider DataServiceProvider => WorkflowInit.DataServiceProvider; public static string ConnectionString => _dbRuntime.ConnectionString; public static List Employees { get; } //The SchemeCode is taken from init scripts for the database public const string SchemeCode = "SimpleWF"; public const string ConfigPath = "./configs"; public const string OptionsConfigName = "options.json"; private const string EmployeesConfigName = "employees.json"; private static readonly DbRuntime _dbRuntime; private static WorkflowRuntime? _workflowRuntime; } ``` Now we have a full-fledged environment for running and working with tests, and we can move on to writing the tests themselves. Your project should now appear somewhat like this: ![Test structure](/documentation/assets/images/test-structure-23e3706d162c69b1c564a925374c55da.png "Test structure") ## Write tests[​](#write-tests "Direct link to Write tests") Before running tests, we need to create a static data in the `TestData` class. To do this, create an `AssemblyInitializer` class in the `./Tests` folder. Learn more about MSTest attributes in the Microsoft [documentation](https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.testtools.unittesting). ``` using Microsoft.VisualStudio.TestTools.UnitTesting; namespace WF.Sample.Tests.Tests; [TestClass] public static class AssemblyInitializer { [AssemblyInitialize] public static async Task AssemblyInit(TestContext context) { await TestData.StartAsync(); } [AssemblyCleanup] public static async Task AssemblyCleanup() { await TestData.StopAsync(); } } ``` ### VacationRequest tests[​](#vacationrequest-tests "Direct link to VacationRequest tests") Now we can create a test class for the VacationRequest scheme. It's best to divide test methods into classes, where the class is responsible for one scheme, and the method for a logical element. Create a class `VacationRequestTests`, for quick access to `WorkflowRuntime`, take it to the properties. ``` using Microsoft.VisualStudio.TestTools.UnitTesting; using OptimaJet.Workflow.Core.Runtime; using WF.Sample.Tests.Mockups; namespace WF.Sample.Tests.Tests; [TestClass] public class VacationRequestTests { public WorkflowRuntime Runtime => TestData.WorkflowRuntime; [TestMethod] public async Task SumSavingTest(double sum) { throw new NotImplementedException(); } [TestMethod] public async Task SchemeRouteTest(int sum, params string[] commandNames) { throw new NotImplementedException(); } [TestMethod] public async Task AvailableCommandsTest(string employee, params string[] commandNames) { throw new NotImplementedException(); } } ``` Create an extension to instantiate processes using a `TestDocument` object and immediately add it to our `TestDocumentRepository` mockup. ``` using OptimaJet.Workflow.Core.Runtime; using WF.Sample.Business.DataAccess; using WF.Sample.Tests.Mockups; namespace WF.Sample.Tests.Tests; public static class Extensions { public static async Task CreateInstanceAsync(this WorkflowRuntime runtime, TestDocument document) { var documents = TestData.DataServiceProvider.Get(); documents.InsertOrUpdate(document.ToDocument()); await runtime.CreateInstanceAsync(TestData.SchemeCode, document.Id); } } ``` ### SumSavingTest[​](#sumsavingtest "Direct link to SumSavingTest") The first test will check that the `Sum` is saved in the process parameters. The test is data-driven and through the `DataRow` attribute, you can pass different options for the `Sum`. In the test we create a document, specifying the author, who has absolute permissions, and `Sum`, then we make an instance by our extension, get the `Sum` using `GetParameterAsync`, and check it with `Assert.AreEqual`. ``` [TestMethod] [DataRow(1.0)] [DataRow(10000.0)] [DataRow(100000000000.0)] [DataRow(1.9)] [DataRow(1.99999)] [DataRow(1.999999999999)] public async Task SumSavingTest(double sum) { var document = new TestDocument(TestData.Employees.First(e => e.Name == "SuperUser")) {Sum = Convert.ToDecimal(sum)}; await Runtime.CreateInstanceAsync(document); var instance = await Runtime.GetProcessInstanceAndFillProcessParametersAsync(document.Id); var sumParameter = await instance.GetParameterAsync("Sum"); Assert.AreEqual(Convert.ToDecimal(sum), sumParameter.Value); } ``` ### SchemeRouteTest[​](#schemeroutetest "Direct link to SchemeRouteTest") In the second test, we'll check the sequence of commands execution, or the route, which will change depending on the specified `Sum`. Similar to the prior test, we use a `DataRow` to pass parameters and `SuperUser` as the author, to which we previously assigned all roles. After instantiation, the commands got by `GetAvailableCommandsAsync` are sequentially executed by `ExecuteCommandAsync`. With the help of `Assert.IsTrue(result.WasExecuted)` we check the success of the command execution, and at the end of the loop we make sure that the process has taken the finalized status. ``` [TestMethod] [DataRow(10, "StartSigning", "Approve", "Paid")] [DataRow(1000, "StartSigning", "Approve", "Approve", "Paid")] public async Task SchemeRouteTest(int sum, params string[] commandNames) { var document = new TestDocument(TestData.Employees.First(e => e.Name == "SuperUser")) {Sum = sum}; await Runtime.CreateInstanceAsync(document); foreach (var commandName in commandNames) { var commands = await Runtime.GetAvailableCommandsAsync(document.Id, document.Author.Id.ToString()); var command = commands.First(c => c.CommandName == commandName); var result = await Runtime.ExecuteCommandAsync(command, document.Author.Id.ToString(), document.Author.Id.ToString()); Assert.IsTrue(result.WasExecuted); } var status = await Runtime.GetCurrentStateAsync(document.Id); Assert.AreEqual("RequestApproved", status.Name); } ``` ### AvailableCommandsTest[​](#availablecommandstest "Direct link to AvailableCommandsTest") In earlier tests, we used an employee who had all the roles. In the third test, we'll check the work of the roles themselves. To do this, we create a document, assign `User` as the author, and `Manager` as the manager. Role names and their associated commands are passed as parameters. We're checking for commands coming from activity `ManagerSigning`, so the first thing we do is jump into it by running `StartSigning`. Next, we get the available commands for the specified employee and do an outer join with the sample commands. If the command lists are equivalent, we get an empty list, what we check with Assert. ``` [TestMethod] [DataRow("BigBoss")] [DataRow("Accountant")] [DataRow("User")] [DataRow("Manager", "Approve", "Reject")] [DataRow("Guest")] public async Task AvailableCommandsTest(string employee, params string[] commandNames) { var document = new TestDocument(TestData.Employees.First(e => e.Name == "User")) { Manager = TestData.Employees.First(e => e.Name == "Manager") }; await Runtime.CreateInstanceAsync(document); var initCommand = (await Runtime.GetAvailableCommandsAsync(document.Id, document.Author.Id.ToString())) .First(c => c.CommandName == "StartSigning"); await Runtime.ExecuteCommandAsync(initCommand, document.Author.Id.ToString(), document.Author.Id.ToString()); var assignee = TestData.Employees.First(e => e.Name == employee); var availableCommandNames = (await Runtime.GetAvailableCommandsAsync(document.Id, assignee.Id.ToString())) .Select(c => c.CommandName).ToList(); var outerJoin = commandNames .Union(availableCommandNames).Distinct() .Except(commandNames.Intersect(availableCommandNames)); Assert.AreEqual(0, outerJoin.Count()); } ``` Now you can run tests from your IDE or console. A running docker is all you need to run tests. ``` dotnet test ``` ## Conclusion[​](#conclusion "Direct link to Conclusion") You can build up your own scheme testing using this example. But it isn't necessary to use the MSTest framework and Testcontainers, you can build the testing architecture that you are used to. The tests are simple to write, isolated, and reliable, as you can see from the guide. Additionally, testing any aspect of business logic is completely free. --- # Integration tips info If you're looking for guidance on integration, you need to read [this article](https://workflowengine.io/documentation/how-to-integrate). ## Overview[​](#overview "Direct link to Overview") When it comes to integrating a more or less complex component into an existing system, several questions immediately arise regarding how to do it better. Workflow Engine is well-suited for integration into both existing and new projects. In fact, when Workflow Engine was initially designed, it was envisioned as a library specifically tailored for existing solutions. In this article, we will discuss some of the most common methods for integrating a Workflow Engine. It's important to note that there is no one-size-fits-all solution. What works best for your application may not be the best solution for someone else's. So, it's essential to consider your specific needs and goals when choosing an integration method. ## Workflow Engine components[​](#workflow-engine-components "Direct link to Workflow Engine components") Workflow Engine consists of several components that can be integrated into your application. The main components are: 1. **Workflow Engine** (nuget packages) - the core of the Workflow Engine, responsible for executing processes. 2. **Workflow Designer** (npm packages) - a tool for creating process schemas. 3. **Workflow Engine Database** - stores process schemas, processes, timers, process parameters, and everything needed to process processes. ![Workflow Engine components](/documentation/assets/images/integration-tips-01-df58272e825e790be815c924720a96c7.png "Workflow Engine components") ### Example of a simple architecture[​](#example-of-a-simple-architecture "Direct link to Example of a simple architecture") ![Example of a simple architecture](/documentation/assets/images/integration-tips-02-fd89f24ad56aa4371c8c323b0544ef5e.png "Example of a simple architecture") ### Backend picture[​](#backend-picture "Direct link to Backend picture") ![Workflow Engine Backend picture](/documentation/assets/images/integration-tips-03-3d1b89602f72df28a96cd45def82cb3b.png "Workflow Engine Backend picture") ### Database picture[​](#database-picture "Direct link to Database picture") ![Workflow Engine Database picture](/documentation/assets/images/integration-tips-04-37371c8ccbe59768c8e4f0c7a0aafde8.png "Workflow Engine Database picture") ## Database[​](#database "Direct link to Database") The Workflow Engine stores process schemas, the processes themselves, and everything needed to process processes in a database. Workflow Engine [supports](https://workflowengine.io/documentation/#supported-databases) the following databases: Microsoft SQL Server, Azure SQL, Azure SQL Managed Instance, PostgreSQL, MongoDB, Azure Cosmos DB, MySQL, Oracle, SQLite. A description of the objects used for the Microsoft SQL Server database can be found [here](https://workflowengine.io/documentation/db-entities). The first decision to make is which database to use for storage. We recommend choosing the database you are most familiar with. If you don't use a cloud solution, you will need a specialist to act as a DBA (database administrator). For .NET solutions, a large number of companies use Microsoft SQL Server. While this solution has some drawbacks, it's a robust and widely used database. info We do not recommend the use of SQLite in industrial solutions. The next decision you need to make is whether to use an existing database or deploy a new one. If you are doing a new project, then deploying a new database is probably the right decision. If, however, you are integrating Workflow Engine into an existing project, then the answer is not so simple. For example, if your application does not yet have a system in place to manage workflows, then it is probably easier to use the existing application database, since you already have backup-restore processes set up for that database. If you already have a process management system in place, and you are considering replacing it with Workflow Engine, you will need to decide if you want to keep everything in one database or if you would prefer to split it up. One option for migration is to create a new database for the new processes and gradually transfer them over to Workflow Engine while keeping the old system running until it can be fully disabled. Recommendation If you have difficulties with the choice, we recommend using one database for both the application and Workflow Engine. The simpler, the better. ### Database updates[​](#database-updates "Direct link to Database updates") Workflow Engine [supports](https://workflowengine.io/documentation/main-terms/database-versioning) automatic updates of used database objects. When you update the Workflow Engine version, you can run scripts automatically, or run them [manually](https://workflowengine.io/documentation/main-terms/database-versioning#migration-list). There is no need to run migration scripts every time the application is launched. You just need to do this once, when updating Workflow Engine version. ## Workflow Designer[​](#workflow-designer "Direct link to Workflow Designer") Workflow Designer is not an essential component for the execution of processes. You can create process schemas in one application and execute them in another, and that's perfectly fine. In fact, all you really need from Designer is an XML file with the process schema, which you can then upload to Workflow Engine. Furthermore, you can create XML workflow schemas without using Designer at all, simply by using [code](https://workflowengine.io/documentation/creating-a-workflow-schema-using-code). In addition, you can choose from several different types of Designers. For example, you can [customize](https://workflowengine.io/documentation/designer-customization/appearance-customization) and simplify the functionality of Designer, and allow your users to design process schemas with a limited number of [activities](https://workflowengine.io/documentation/scheme/activities). At the same time, you may have another, fully functional Designer that is available only to system administrators. Based on the above, choose whether you need to connect Designer to the application that executes the processes or not. Recommendation In a simple case, connect a Designer with built-in Activities to your application without any modifications. If necessary, restrict access to Designer. ## Security[​](#security "Direct link to Security") Workflow Engine does not have its own security system, instead Workflow Engine uses your security system. You only need to [connect your security modules](https://workflowengine.io/documentation/scheme/rules) to Workflow Engine. If you need Active Directory integration, [this plugin](https://workflowengine.io/documentation/plugins/activedirectoryplugin) might come in handy. ## Actions[​](#actions "Direct link to Actions") There are two types of actions: actions in the schema and actions in the code of your application. The main difference between them is that actions in the schema are versioned with the schema. You should use actions in the schema when you want to version and forward the action code along with the schema, or when you want to share the schema with someone else without sharing the application code. Actions in the schema go through a compilation process that takes memory and CPU time. Recommendation In most cases, it is better to keep the action code in the application and version it along with the application releases. This will allow you to easily update the action code without having to update the entire schema with each change. ## Parameters[​](#parameters "Direct link to Parameters") If you need to store large amounts of data in parameters, it is worth considering using [external parameters](https://workflowengine.io/documentation/scheme/parameters#external). ## Single-Server or Multi-Server mode[​](#single-server-or-multi-server-mode "Direct link to Single-Server or Multi-Server mode") In most cases, you will only need to use the [Single-Server mode](https://workflowengine.io/documentation/scalability/singleserver). The [Multi-Server mode](https://workflowengine.io/documentation/scalability/multiserver) is needed if your application is running in a cluster and stopping it is critical. Multi-Server allows Workflow Engine to run in a cluster, which means you can update your application without stopping (by updating the cluster nodes one after another). Also, Multi-Server allows you to distribute the load between application instances. ## Multi-tenancy[​](#multi-tenancy "Direct link to Multi-tenancy") Workflow Engine [supports multitenancy](https://workflowengine.io/documentation/scalability/multitenancy). If you require this feature, you are likely already familiar with application architecture. ## HTTP API[​](#http-api "Direct link to HTTP API") Workflow Engine has a Web API that allows you to manage processes via HTTP requests. [Read more about it here](https://workflowengine.io/documentation/web-api). --- # License key Important This functionality is available starting with Workflow Engine 9.0.0. To obtain a key for earlier versions of Workflow Engine, please email the Sales Department at . ## Obtaining a license[​](#obtaining-a-license "Direct link to Obtaining a license") The procedure for obtaining a license or trial key is simple and consists of the following steps: 1. You must install and run Workflow Engine. For information on how to start Workflow Engine, see [this section](https://workflowengine.io/documentation/how-to-integrate) of the documentation. 2. Get information about the installed copy through Workflow Designer: 1. Open Workflow Designer in your browser. 2. Select Menu -> Instance Info: ![Instance info menu](/documentation/assets/images/instance-info-01-28df6eba0707d256e63f4ba52ccebcc1.png "Instance info menu") 3. The information about the installed Workflow Engine will be copied to the clipboard (if your browser supports the clipboard feature) and displayed in the browser console: ![Instance info](/documentation/assets/images/instance-info-02-531cb08760751a07cd9bd2853c32b04e.png "Instance info") 3. Send information about the installed copy of Workflow Engine from the clipboard or browser console to the Sales Department at . Example of information about a running Workflow Engine: ``` { "data": "{\"processArchitecture\":\"Arm64\",\"frameworkDescription\":\".NET 6.0.12\",\"osArchitecture\":\"Arm64\",\"osDescription\":\"Darwin 22.4.0 Darwin Kernel Version 22.4.0: Mon Mar 6 20:59:28 PST 2023; root:xnu-8796.101.5~3/RELEASE_ARM64_T6000\",\"macAddress\":\"BEB050D33178\"}", "hash": "ZQSh2EOw8Iio6p9L01l82oMqjU7d72LdGrO3qg7RrIU05c7FJm6bAL6IiElZEprimiDpyJwPsU4EHFmgKIYN+Q==" } ``` ### Obtaining a trial key via an AI agent[​](#obtaining-a-trial-key-via-an-ai-agent "Direct link to Obtaining a trial key via an AI agent") You can also obtain a trial key through an AI agent (such as Claude, Codex, GitHub Copilot, Cursor, or other LLM-based coding assistants). To do so, provide the agent with the contents of the following files from [`trial.workflowengine.io`](https://trial.workflowengine.io): * [llms.txt](https://trial.workflowengine.io/llms.txt) — an overview of the trial key generation process and available options. * [llms-full.txt](https://trial.workflowengine.io/llms-full.txt) — a detailed guide for AI agents covering the complete trial key generation flow, step-by-step instructions, and all required parameters. The agent will use these documents to guide you through obtaining a trial key without manually contacting support. ## Registering a license key in Workflow Engine[​](#registering-a-license-key-in-workflow-engine "Direct link to Registering a license key in Workflow Engine") How you register the license key depends on how you integrate Workflow Engine into your application. ### Workflow Engine Core[​](#workflow-engine-core "Direct link to Workflow Engine Core") If you use Workflow Engine Core directly and create `WorkflowRuntime` manually, register the license key before starting Workflow Engine: ``` WorkflowRuntime.RegisterLicense("your license key text"); ``` ### Workflow Engine Web API[​](#workflow-engine-web-api "Direct link to Workflow Engine Web API") If you use [Workflow Engine Web API](https://workflowengine.io/documentation/web-api), set the license key in `WorkflowApiCoreOptions` when configuring services in `Program.cs`: ``` builder.Services.AddWorkflowApiCore(SetupWorkflowApiCore); void SetupWorkflowApiCore(WorkflowApiCoreOptions options) { options.LicenseKey = "Put your license key here"; } ``` --- # WorkflowRuntime: Process Management This Section describes basic workflow management functionality. In fact, there are far more ways of managing workflows through Workflow Engine, however, in most cases you are likely to apply only these methods. ## Process Creation[​](#creation "Direct link to Process Creation") A minimum version of the `CreateInstanceAsync` method accepts two parameters: scheme code (name) and process ID: ``` await WorkflowInit.Runtime.CreateInstanceAsync("SchemeCode", processId); ``` The most extensive version of the `CreateInstanceAsync` method accepts `CreateInstanceParams`. This object contains supplementary input parameters for process creation: ``` var createInstanceParams = new CreateInstanceParams("SchemeCode", processId) { InitialProcessParameters = new Dictionary() { {"Parameter1", parameterValue} }, IdentityId = identityId, ImpersonatedIdentityId = impersonatedIdentityId }; await WorkflowInit.Runtime.CreateInstanceAsync(createInstanceParams); ``` * `InitialProcessParameters` is a process parameters dictionary that is passed during the process creation; * `IdentityId` and `ImpersonatedIdentityId` are identifiers of the user performing an operation and the one on whose behalf it is performed. These attributes should be transferred during the creation stage only if the process demands so right after the start; * `ImpersonatedIdentityId` should be transferred only if you use impersonation (user performs operations on behalf of another user), otherwise it doesn't have to be transferred. During workflow instance creation the only required parameters you have to fill are the scheme code (name) and process ID. ## Commands Retrieval and Execution[​](#execution "Direct link to Commands Retrieval and Execution") The `GetAvailableCommandsAsync` method that accepts process ID and user ID, it can be called in order to get a list of commands available to a user. The `IEnumerable` retrieves a list of commands available for a particular user, enquiring process ID and user identity ID: ``` var commandsList = await WorkflowInit.Runtime.GetAvailableCommandsAsync(processId, identityId); ``` Here, `WorkflowCommand` contains information on original and localized command names, along with parameters that should be transferred along with the command. This object supports serialization both in JSON and XML formats, thus, enabling you to easily retrieve it from your services. You should invoke the method `ExecuteCommandAsync` to execute a command as indicated below: ``` await WorkflowInit.Runtime.ExecuteCommandAsync(command, identityId, impersonatedIdentityId); ``` The `identityId` and `impersonatedIdentityId` parameters define the user performing the operation and the one on whose behalf it is performed. If you don't use impersonation, simply submit `identityId` as `impersonatedIdentityId`. Indeed, `command` is a command retrieved by calling the `GetAvailableCommandsAsync` method. A use case for web application that already contains client data validation looks like this: ``` var processId = ...; // Get it from a request var commandName = ...; // Get it from a request var currentUser = HttpContext.User.Identity.Name; var commands = await WorkflowInit.Runtime.GetAvailableCommandsAsync(processId, currentUser); var command = commands.FirstOrDefault(c => c.CommandName == commandName); if (command == null) return; // Or throw an error await WorkflowInit.Runtime.ExecuteCommandAsync(command, currentUser, currentUser); ``` Furthermore, you may enquire for user login, database GUID or anything else in order to identify the user's `identityId`. Two additional methods were added into Workflow Engine ≥ 4.0 - getting a list of available commands and command execution: * getting a list of available commands with additional conditions check ``` var commands = await WorkflowInit.Runtime.GetAvailableCommandsWithConditionCheckAsync(processId, currentUser); ``` In addition to restrictions, these methods checks all conditions linked to corresponding command triggered transitions. * command execution with additional restrictions check ``` await WorkflowInit.Runtime.ExecuteCommandWithRestrictionCheckAsync(command, currentUser, impersonatedUser); ``` The standard method `ExecuteCommandAsync` does not perform restrictions secondary check, whilst the `ExecuteCommandWithRestrictionCheckAsync` performs additional check whether command transition is available for user `impersonatedUser`. example An example that demonstrates how can be used `ExecuteCommand` method in `WorkflowController` class in a sample application, it can be found in our [Tutorials section](https://workflowengine.io/documentation/process-parameters-sample#workflowcontroller). ## Workflow State Retrieval and Change[​](#state "Direct link to Workflow State Retrieval and Change") The `GetAvailableStateToSetAsync` method which accepts process ID, it can be called to get a list of available states. ``` var statesList = await WorkflowInit.Runtime.GetAvailableStateToSetAsync(processId); ``` The `WorkflowState` contains information on original and localized state names. This object supports serialization both in JSON and XML formats, enabling you to easily retrieve it from your services. The `SetStateAsync` method is applied for setting up the process state. It accepts a `SetStateParams` object that contains all necessary parameters: ``` var setStateParams = new SetStateParams(processId, stateName) { IdentityId = identityId, ImpersonatedIdentityId = impersonatedIdentityId, PreventExecution = false }; setStateParams.AddPersistentParameter("Parameter1", parameterValue); await WorkflowInit.Runtime.SetStateAsync(setStateParams); ``` The `SetStateParams` constructor accepts `processId` and `stateName` as required parameters. Additional properties: * `ProcessId` - Process id; * `StateName` - State name to set forcibly; * `IdentityId` - The user id which execute operation; * `ImpersonatedIdentityId` - The user id for whom executes operation; * `ProcessParameters` - Parameters to be passed to the process; * `Persist` - Names of persist parameters in ProcessParameters; * `PreventExecution` - Actions due to transition process do not executed if true. If this parameter is set to false, then `WorkflowEngine` will execute the `Implementation` section of the initial `Activity` of the current state, and start the transient process if possible. In case `PreventExecution` is set to true, the current state and activity will be changed, while no execution will be performed. You can add parameters using `AddPersistentParameter` method (for persisted parameters) or `AddTemporaryParameter` method (for temporary parameters). The minimum parameter set of the `SetStateAsync` function includes: ``` var setStateParams = new SetStateParams(processId, stateName); await WorkflowInit.Runtime.SetStateAsync(setStateParams); ``` ## Process Deletion[​](#deletion "Direct link to Process Deletion") The `DeleteInstanceAsync` method removes the entire process with the associated artifacts from persistence storage: ``` await WorkflowInit.Runtime.DeleteInstanceAsync(processId); ``` ## Async API[​](#async "Direct link to Async API") info Starting from Workflow Engine version 21.0, **WorkflowRuntime provides only asynchronous API**. All synchronous methods have been removed. If you need synchronous behavior, you can use the `.GetAwaiter().GetResult()` pattern on async methods: ``` // Synchronous call example WorkflowInit.Runtime.CreateInstanceAsync("SchemeCode", processId).GetAwaiter().GetResult(); ``` However, **it's strongly recommended to use async/await** throughout your application for better performance and scalability. ## Base Events[​](#base "Direct link to Base Events") In day-to-day work you may need 2 events: ``` runtime.OnProcessActivityChanged += (sender, args) => { }; runtime.OnProcessStatusChanged += (sender, args) => { }; ``` `OnProcessActivityChanged` event is raised every time the current process activity is changed. Two objects are passed in event arguments: `ProcessInstance` with all process parameters and `CurrentActivity` defining the updated current activity. `TransitionalProcessWasCompleted` attribute signifies whether or not the transient process is completed. If it is, then the process stops, awaiting for a command or timer. It is appropriate to fill in the `WorkflowInbox` table in this event-handler. `OnProcessStatusChanged` event happens, when the process status is changed, for example if running-to-idled transition (and vice versa) has taken place. Event arguments contain the former and new status data, giving access to the `ProcessInstance` object. This event-handler enables you to delete the process once it reached the finalized status. --- # Database Versioning Starting from version 13.0.0, Workflow Engine supports automatic migration, enabling the creation and updating of the database schema without manual script execution. ## Workflow Engine migrations[​](#workflow-engine-migrations "Direct link to Workflow Engine migrations") To initiate Workflow Engine migrations, execute the `RunMigrations` method when configuring the WorkflowRuntime after adding the PersistentProvider. This method will create a database schema or update an existing one. ``` _runtime .WithPersistenceProvider(provider) .RunMigrations(); ``` info If you want to enable logging during the migration process, make sure to configure the logging for the runtime before calling `RunMigrations()`. ## Custom Migrations[​](#custom-migrations "Direct link to Custom Migrations") You can create custom migrations by creating a class that inherits from `Migration` and adding the Migration attribute with the migration order number. info To maintain migration order, start your migrations with the number `2_000_000`. For managing migrations, Fluent.Migrator is used. For more details on creating migrations, refer to the [official documentation](https://fluentmigrator.github.io/). ## Migration List[​](#migration-list "Direct link to Migration List") You can retrieve a list of all registered migrations and a list of pending migrations using the WorkflowRuntime methods `GetMigrationNames` and `GetUnappliedMigrationNames` respectively. You can also retrieve the SQL script text by specifying the migration name. ``` var migrationNames = _runtime.GetMigrationNames().ToList(); migrationNames.ForEach(scriptName => { Console.WriteLine($"Script: {scriptName}"); string sql = _runtime.GetMigrationSql(scriptName); Console.WriteLine(sql); }); ``` Additionally, if your migrations utilize SQL scripts as embedded resources, you can also retrieve a list of your migrations along with Workflow Engine migrations. To achieve this, add the WorkflowEngineMigration attribute to your migrations with a reference to the resource. ``` [Migration(2000000)] [WorkflowEngineMigration("WF.Sample.Business.Scripts.Initial.sql")] public class Migration2000000Initial : Migration { public override void Up() { this.EmbeddedScript(); } public override void Down() { } } ``` --- # Designer ## Examples of Designer integration[​](#examples "Direct link to Examples of Designer integration") * [WorkflowEngine Designer for JavaScript Sample](https://github.com/optimajet/workflow-designer-javascript-sample) * [WorkflowEngine Designer for React Sample](https://github.com/optimajet/workflow-designer-react-sample) * [WorkflowEngine Designer for Angular Sample](https://github.com/optimajet/workflow-designer-angular-sample) # Designer In order to include the visual scheme designer into your application you should create a Javascript object — `WorkflowDesigner` — on a web page where you want the designer to be displayed, and a function that will process requests from the designer and pass them to `WorkflowRuntime`. ![Workflow Designer](/documentation/assets/images/scheme-wfe-1-b2824ed314c201a25d9dd9ab847c9dcb.png) ## Frontend[​](#frontend "Direct link to Frontend") Add links to the following JavaScript libraries on the web page: * [JQuery](https://jquery.com/) * [workflowdesigner.min.js](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/workflowdesigner.min.js) — the designer itself. You should also add one style sheet: * [workflowdesigner.min.css](https://github.com/optimajet/WorkflowEngine.NET/blob/master/Designer/workflowdesigner.min.css) — the designer's style sheet; note If you need to separate CSS and JavaScript (for example, to meet a strict CSP), use the no-CSS-in-JS entrypoint and include the stylesheet separately. See the [How to satisfy strict CSP in Workflow Designer?](https://workflowengine.io/documentation/faq/workflow-engine/strict-csp-designer) Besides, put designer's [templates](https://github.com/optimajet/WorkflowEngine.NET/tree/master/Designer/templates) into the templates folder. If you downloaded the [workflow-engine-net-core.zip](https://workflowengine.io/downloads/net-core/) archive, you will find the above-mentioned Javascript libraries and templates in the Designer folder. You can download Jquery via the link above. If you installed the designer from NuGet, then you have all links already written in Designer/index.chtml, except for those to JQuery. You have to manually put links to those library versions that you have in the project. ### Designer properties[​](#designer-properties "Direct link to Designer properties") Once all the necessary libraries are installed, create the JavaScript object `WorkflowDesigner`. ``` wfdesigner = new WorkflowDesigner({ name: 'simpledesigner', apiurl: '/Designer/API', renderTo: 'wfdesigner', graphwidth: graphwidth, graphheight: graphheight }); ``` The following settings are required: * `apiurl: '/Designer/API'` — path for submitting requests to the server part. In this particular case, it’s the DesignerController API method; * `renderTo: 'wfdesigner'` — ID of the div the scheme is drawn into; * `graphwidth` — designer's canvas width; * `graphheight` — designer's canvas height; You can also define optional settings: ``` wfdesigner = new WorkflowDesigner({ templatefolder: '/templates/', cacheLoadedFiles: false, showSaveButton: true, notrendertoolbar: true, notshowwindows: true, disableobjectmovements: true, mode: 'printable', logo: 'logo.png', logoPosition: 'bottom-right', }); ``` * `templatefolder` — path to the templates (all forms) folder; * `cacheLoadedFiles` — caches downloaded files (e.g. templates), enabled by default. * `showSaveButton` — shows the save button on the toolbar; * `notrendertoolbar` — toolbar is not rendered in the read-only mode; * `notshowwindows` — object properties that are generally shown via a double click on Activity or Transition are restricted in the read-only mode; * `disableobjectmovements` — object movement is not available in the read-only mode; * `mode: 'printable'` — enables print mode. Grid, toolbar and all control elements are hidden, while the canvas scales automatically to show the entire scheme on canvas. This mode allows you to render a pdf document directly from the browser. * `logo` - the path to your logo, which will be displayed in the designer if it is included in your license. * `logoPosition` - The position of the logo in the designer. It takes the following values: 'top-left', 'top-right', 'button-left', 'button-right'. You can also configure designer API call headers: 1. Set up ajax pipeline in the main app class by handling the beforeSend event. ``` window['$'].ajaxSetup({beforeSend: this.onSend}); ``` 2. Add a handler function that adds custom headers to the request. ``` // Example for bearer authorization function onSend(e, data) { const token = 'myToken'; e.setRequestHeader('Authorization', `Bearer ${token}`); return true; } ``` Core `WorkflowDesigner` functions: * Scheme existence check: if the scheme already exists, it is opened, otherwise a new scheme is created. ``` var schemecode = QueryString.schemecode; var processid = QueryString.processid; var readonly = processid !== undefined; var p = { schemecode: schemecode, processid: processid, readonly: readonly }; if (wfdesigner.exists(p)) { wfdesigner.load(p); } else { wfdesigner.create(); } ``` Function `exists(p)` checks whether the scheme exists. Function `load(p)` loads the scheme to the designer. You can pass either the scheme code or process ID to both of them. If process ID is entered, then `WorkflowDesigner` opens the scheme associated with the process. If scheme code is passed, it takes the required one from the `WorkflowScheme` table. Yet, there is another way: you can enter the `schemeid` and retrieve the scheme from `WorkflowProcessScheme`. Parameter `readonly` defines the read-only mode for the scheme. When its enabled all objects are not available for editing. `create()` is used to create a new blank scheme. * Saving a scheme: ``` wfdesigner.schemecode = schemecode; var err = wfdesigner.validate(); if (err != undefined && err.length > 0) { alert(err); } else { wfdesigner.save(function () { alert('The scheme was saved!'); }); } ``` First, assign the scheme code for storing the item in the `WorkflowScheme` table. Then invoke the `validate()` function to check the scheme against errors. Finally, invoke the `save(success)` function that receives a function once the scheme is successfully saved. * Uploading a scheme to designer: ``` wfdesigner.uploadscheme($('#uploadform')[0], function () { alert('The file was uploaded!'); }); ``` Call the `uploadscheme(form, success)` function, attaching a form that contains the uploaded file and function triggered by successful upload of the scheme. Keep in mind, that when this function is called, the scheme is loaded into designer object, but it is not saved in a database. Use the `save()` function to save your scheme. In this case, the scheme is not saved in the database. * Downloading a scheme from designer: ``` wfdesigner.downloadscheme(); ``` The scheme is downloaded from designer to your computer as an XML file. * Deleting the designer object: ``` wfdesigner.destroy(); ``` * Accessing the scheme displayed in designer: ``` var data = wfdesigner.data; ``` * Designer re-rendering: ``` wfdesigner.render(); ``` * Switching designer's view mode: ``` wfdesigner.readonlymode(); wfdesigner.printablemode(); wfdesigner.editablemode(); ``` In order to add localization to designer you will have to add another JavaScript file — workflowdesigner.localization.js — that contains all the string constants that are used by the designer. If you want to translate the designer's interface to another language you have to do it yourself. This file should be included after workflowdesigner.min.js. ## Backend[​](#backend "Direct link to Backend") There should be a method transferring requests from designer to `WorkflowRuntime` on the server side. Here is an example of such a method for ASP.NET MVC Core: ``` public class DesignerController : Controller { public IActionResult API() { Stream filestream = null; var isPost = Request.Method.Equals("POST", StringComparison.OrdinalIgnoreCase); if (isPost && Request.Form.Files != null && Request.Form.Files.Count > 0) filestream = Request.Form.Files[0].OpenReadStream(); var pars = new NameValueCollection(); foreach (var q in Request.Query) { pars.Add(q.Key, q.Value.First()); } if (isPost) { var parsKeys = pars.AllKeys; foreach (var key in Request.Form.Keys) { if (!parsKeys.Contains(key)) { pars.Add(key, Request.Form[key]); } } } var res = WorkflowInit.Runtime.DesignerAPI(pars, out bool hasError, filestream, true); if (pars["operation"].ToLower() == "downloadscheme" && !hasError) return File(Encoding.UTF8.GetBytes(res), "text/xml"); return Content(res); } } ``` Here is an example of such a method for ASP.NET MVC: ``` public class DesignerController : Controller { public ActionResult API() { Stream filestream = null; if (Request.Files.Count > 0) filestream = Request.Files[0].InputStream; var pars = new NameValueCollection(); pars.Add(Request.QueryString); if (Request.HttpMethod.Equals("POST", StringComparison.InvariantCultureIgnoreCase)) { var parsKeys = pars.AllKeys; foreach (string key in Request.Form.Keys) { if (!parsKeys.Contains(key)) { pars.Add(key, Request.Unvalidated[key]); } } } var res = WorkflowInit.Runtime.DesignerAPI(pars, out bool hasError, filestream, true); var operation = pars["operation"].ToLower(); if (operation == "downloadscheme" && !hasError) return File(Encoding.UTF8.GetBytes(res), "text/xml"); return Content(res); } } ``` There is nothing spectacular in this function, the request parameters are transferred to the `WorkflowInit.Runtime.DesignerAPI(parameters, filestream, true);` function. You should combine the query string parameters with those transferred in the form, and pass them in the `parameters` variable. If the request contains a file, you get it streamed and pass in the `filestream` variable. The last parameter passed to `DesignerApi` is the one indicating whether the `IsObsolete` attribute will be assigned to the existing scheme after a new version is saved. If true, processes created earlier on scheme with the same code will be lazily updated. Here is an example of a similar function where requests are processed by a web service. Such a method is suitable for ASP.NET Web Forms: ``` public class WFEDesigner : IHttpHandler { public void ProcessRequest(HttpContext context) { Stream filestream = null; if (context.Request.Files.Count > 0) filestream = context.Request.Files[0].InputStream; var pars = new NameValueCollection { context.Request.QueryString }; if (context.Request.HttpMethod.Equals("POST", StringComparison.InvariantCultureIgnoreCase)) { var parsKeys = pars.AllKeys; foreach (var key in context.Request.Form.AllKeys) { if (!parsKeys.Contains(key)) { pars.Add(key, context.Request.Unvalidated[key]); } } } var res = WorkflowInit.Runtime.DesignerAPI(pars, out bool hasError, filestream, true); context.Response.Cache.SetNoStore(); if (pars["operation"].ToLower() == "downloadscheme" && !hasError) { context.Response.ContentType = "file/xml"; context.Response.BinaryWrite(Encoding.UTF8.GetBytes(res)); context.Response.End(); } else { context.Response.Write(res); context.Response.End(); } } } ``` --- # Persistence Provider Initialization First, it is necessary to determine whether you need an SQL or NoSQL database and download the necessary provider from the [downloads](https://workflowengine.io/downloads/net/) page or via NuGet. You will find provider's source code in the downloaded archive or in our GitHub [repository](https://github.com/optimajet/WorkflowEngine.NET). If you use an RDMS such as MS SQL Server, PostgreSQL, Oracle or MySql you have to add a command to run migrations, that creates the following tables in the database: * `WorkflowProcessScheme` — all process schemes are kept here. The `Scheme` column contains a serialized XML scheme, the `SchemeCode` column has the scheme name (code). `IsObsolete` indicates the scheme's relevance: if it's relevant, new processes will rest on it (read the Schemes Update section). Other columns contain data for creating subprocesses (read about parallel branches in the Parallel Branches section). * `WorkflowProcessInstance` — contains process data, such as the current activity and state, previous activities, etc. There is also IDs of parent and root processes here. * `WorkflowProcessInstancePersistence` — this table keeps process parameters. Parameter values are serialized in JSON and shown in the `Value` column. * `WorkflowProcessTransitionHistory` — contains information about successful process transitions. The table is used for process tracking. * `WorkflowProcessInstanceStatus` — the `Status` field indicates current process status: 0 - Initialized, 1 - Running, 2 - Idled, 3 - Finalized. * `WorkflowProcessTimer` — contains trigger actuation times for different processes. * `WorkflowGlobalParameter` — global Code Actions and subprocess trees are stored here. * `WorkflowScheme` — this table is used by the scheme generator by default. The scheme from designer is saved here; if **WorkflowRuntime** can't find a relevant scheme in the `WorkflowProcessScheme` table, it grabs the scheme from here. * `WorkflowInbox` — doesn't belong to Workflow Engine but is used in our examples as the inbox folder. * `WorkflowRuntime` - information about all workflow runtimes connected to this database is kept here. * `WorkflowSync` - is used in the multi-server mode for timer synchronization. If you use Oracle, the tables will have different names: * `WORKFLOWPROCESSTRANSITIONH` instead of `WorkflowProcessTransitionHistory`; * `WORKFLOWPROCESSINSTANCEP` instead of `WorkflowProcessInstancePersistence`; * `WORKFLOWPROCESSINSTANCES` instead of `WorkflowProcessInstanceStatus`; Furthermore, all data are kept structurally in similar objects with MongoDB. Take a look at the example of persistence provider initialization that you will need in the [next section](https://workflowengine.io/documentation/main-terms/runtime). The code for relational databases is quite similar. ## .NET Framework[​](#net-framework "Direct link to .NET Framework") The *ConnectionString* initialization for **WorkflowRuntime** should be specified and use it at the start of the application. * MS SQL Server * MySQL * PostgreSQL * Oracle * MongoDB ``` // Get a DB connection string var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; // Initiate a provider var dbProvider = new MSSQLProvider(connectionString); ``` ``` // Get a DB connection string var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; // Initiate a provider var dbProvider = new MySQLProvider(connectionString); ``` ``` // Get a DB connection string var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; // Initiate a provider var dbProvider = new PostgreSQLProvider(connectionString); ``` ``` // Get a DB connection string var connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString; // Initiate a provider var dbProvider = new OracleProvider(connectionString); ``` ``` // Get DB's URL and name from config var url = ConfigurationManager.AppSettings["Url"]; var databaseName = ConfigurationManager.AppSettings["DatabaseName"]; // Create a MongoDatabase object var mongoDatabase = new MongoClient(url).GetServer().GetDatabase(databaseName); // Initiate a provider var dbProvider = new MongoDBProvider(mongoDatabase); ``` In addition, you should specify the connection string in the application configuration file `web.config` depending on your database provider and according to the following syntax. ``` ``` ## .NET Core and .NET[​](#net-core-and-net "Direct link to .NET Core and .NET") To connect to the database you need the following: 1. A connection string to connect to the database. For example, you can describe it in `appsettings.json` in the ASP.NET application. 2. Create an instance of the database provider. 3. Pass the database provider instance when creating the Workflow Engine. Below are examples for different databases (ASP.NET): * MS SQL Server * MySQL * PostgreSQL * Oracle * MongoDB ``` { "ConnectionStrings": { "DefaultConnection": "Data Source=(local);Initial Catalog=wfe_sample;User ID=sa;Password=1" } } ``` ``` var connectionString = app.Configuration.GetConnectionString("DefaultConnection"); var provider = new MSSQLProvider(connectionString); var runtime = new WorkflowRuntime() .WithPersistenceProvider(provider) .RunMigrations(); ``` ``` { "ConnectionStrings": { "DefaultConnection": "server=127.0.0.1;uid=root;pwd=myPassword;database=wfe_sample" } } ``` ``` var connectionString = app.Configuration.GetConnectionString("DefaultConnection"); var provider = new MySQLProvider(connectionString); var runtime = new WorkflowRuntime() .WithPersistenceProvider(provider) .RunMigrations(); ``` info The root user does not have a default password. You either need to change the connection setting in configuration file or change the root account to match the password. ``` { "ConnectionStrings": { "DefaultConnection": "User ID=postgres;Password=1;Host=localhost;Port=5432;Database=wfe_sample;" } } ``` ``` var connectionString = app.Configuration.GetConnectionString("DefaultConnection"); var provider = new PostgreSQLProvider(connectionString); var runtime = new WorkflowRuntime() .WithPersistenceProvider(provider) .RunMigrations(); ``` ``` { "ConnectionStrings": { "DefaultConnection": "Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY = tcp.world)(PROTOCOL = TCP)(Host = MyHost)(Port = 1521)))(CONNECT_DATA=(SID=MyOracleSID)));User ID=myUsername;Password=myPassword;" } } ``` ``` var connectionString = app.Configuration.GetConnectionString("DefaultConnection"); var provider = new OracleProvider(connectionString); var runtime = new WorkflowRuntime() .WithPersistenceProvider(provider) .RunMigrations(); ``` ``` { "ConnectionStrings": { "DefaultConnection": "mongodb://localhost:27017/WorkflowEngine" } } ``` ``` var connectionString = app.Configuration.GetConnectionString("DefaultConnection"); var dbName = MongoUrl.Create(connectionString).DatabaseName; var client = new MongoClient(connectionString); var provider = new MongoDBProvider(client.GetDatabase(dbName)); var runtime = new WorkflowRuntime() .WithPersistenceProvider(provider); ``` The next section outlines **WorkflowRuntime** object initialization and creation. --- # WorkflowRuntime Initialization The second thing you should do, when integrating Workflow Engine into your project, is to initialize the `WorkflowRuntime` object. Generally, only one `WorkflowRuntime` object is required so that you can write the thread-safe code for lazy initialization. Here is an example: ``` using System; using System.Xml.Linq; using OptimaJet.Workflow.Core.Builder; using OptimaJet.Workflow.Core.Bus; using OptimaJet.Workflow.Core.Runtime; using OptimaJet.Workflow.DbPersistence; namespace Business { public static class WorkflowInit { private static readonly Lazy LazyRuntime = new Lazy(InitWorkflowRuntime); public static WorkflowRuntime Runtime { get { return LazyRuntime.Value; } } private static WorkflowRuntime InitWorkflowRuntime() { // TODO If you have a license key, you have to register it here //WorkflowRuntime.RegisterLicense("your license key text"); // TODO If you are using database different from SQL Server // you have to use different persistence provider here. var connectionString = System.Configuration.ConfigurationManager .ConnectionStrings["ConnectionString"].ConnectionString; var dbProvider = new MSSQLProvider(connectionString); var builder = new WorkflowBuilder( dbProvider, new OptimaJet.Workflow.Core.Parser.XmlWorkflowParser(), dbProvider ).WithDefaultCache(); // plugin setup var plugin = new OptimaJet.Workflow.Core.Plugins.BasicPlugin(); var runtime = new WorkflowRuntime() .WithBuilder(builder) .WithPersistenceProvider(dbProvider) .EnableCodeActions() .SwitchAutoUpdateSchemeBeforeGetAvailableCommandsOn() .WithPlugin(plugin) // plugin connect .AsSingleServer(); // .AsMultiServer(); // events subscription runtime.OnProcessActivityChanged += (sender, args) => { }; runtime.OnProcessStatusChanged += (sender, args) => { }; // TODO If you have planned to use Code Actions functionality that required references // to external assemblies you have to register them here //runtime.RegisterAssemblyForCodeActions(Assembly.GetAssembly(typeof(SomeTypeFromMyAssembly))); // starts the WorkflowRuntime // TODO If you have planned use Timers the best way to start WorkflowRuntime is somewhere outside // of this function in Global.asax for example runtime.Start(); return runtime; } } } ``` Now you call `WorkflowRuntime` object methods in the following way: ``` var commands = await WorkflowInit.Runtime.GetAvailableCommandsAsync(processId, currentUserId); ``` Here you can stop and simply copy-paste this code into your application, replacing the persistence provider with the relevant one for your database, and it will work. But if you want to understand the way objects are initialized, keep reading. We will probably inspire you towards non-standard efficient solutions. Runtime initialization is described in the **InitWorkflowRuntime** function, let's analyze it line by line. ``` // TODO If you have a license key, you have to register it here //WorkflowRuntime.RegisterLicense("your license key text"); ``` Insert your license key into the **RegisterLicense** method call to remove the restrictions of a Personal license. ``` var connectionString = System.Configuration.ConfigurationManager .ConnectionStrings["ConnectionString"].ConnectionString; var dbProvider = new MSSQLProvider(connectionString); ``` We receive the database connection string from a configuration file and create a **dbProvider** object — a mediator between the database and **WorkflowRuntime**. In this case, it's MS SQL server. Each of persistence provider's classes implements three interfaces, that's why **dbProvider** is further mentioned three times. Here are the interfaces implemented: * `IPersistenceProvider` — **WorkflowRuntime** uploads and downloads a persisted state from the database through this interface; * `ISchemePersistenceProvider` — **WorkflowRuntime** uses it for uploading and downloading process schemes. Specified by XElement type, it indicates the scheme's XML format; * `IWorkflowGenerator` — allows for new scheme generation. In case of using a basic persistence provider, the schemes are downloaded from the `WorkflowScheme` table. ``` var builder = new WorkflowBuilder( dbProvider, new OptimaJet.Workflow.Core.Parser.XmlWorkflowParser(), dbProvider ).WithDefaultCache(); ``` In this part, we create a `WorkflowBuilder` — a critical component that enables scheme upload management, generation, update and parsing as well as process initialization. The object incrementally aggregates the following components: * `IWorkflowGenerator` for scheme generation; * `XmlWorkflowParser` for transforming XML schemes into ProcessDefinition objects; * `IWorkflowGenerator` (XElement tag indicates XML format). Then you call the `.WithDefaultCache()` method that builds cache for parsed process schemes. Though it seems complicated, in practice you hardly have to change this code. All these interfaces and classes are points of expansion of Workflow Engine's functionality. For example, you may want to store your schemes in another format or to use a powerful mechanism of scheme generation. For example, you can add an activity or a transition to existing schemes. We will describe all these capabilities in the next sections. We create the **WorkflowRuntime** object and start configuring it with the following line: ``` var runtime = new WorkflowRuntime() ``` Let's assign the WorkflowBuilder object: ``` .WithBuilder(builder) ``` ... and a persistence provider: ``` .WithPersistenceProvider(dbProvider) ``` If you plan to use Action Provider or Rule Provider, uncomment the following code. You can read more about [ActionProvider](https://workflowengine.io/documentation/scheme/actions) and [RuleProvider](https://workflowengine.io/documentation/scheme/rules) in respective sections of documentation. ``` // TODO If you are using Action Provider uncomment following line of code //.WithActionProvider(new ActionProvider()) // TODO If you are using Rule Provider uncomment following line of code //.WithRuleProvider(new RuleProvider()) ``` Let's enable the Code Actions functionality — writing code for managing Actions inside the scheme: ``` .EnableCodeActions() ``` Thereafter, we turn on automated updates of schemes marked with IsObsolete. The engine applies a lazy update method before it gets the list of available commands: ``` .SwitchAutoUpdateSchemeBeforeGetAvailableCommandsOn(); ``` Finally, setup you runtime for the single-server or multi-server environment: * Use the following call for the single-server environment: ``` runtime.AsSingleServer(); ``` * Use the following call for the multi-server environment: ``` runtime.AsMultiServer(); ``` You may need to subscribe for **WorkflowRuntime** events to implement your business logic; it is better to do it here: ``` // events subscription runtime.OnProcessActivityChanged += (sender, args) => { }; runtime.OnProcessStatusChanged += (sender, args) => { }; ``` If you are planning to use the Code Actions functionality that requires references to external assemblies, you have to register the assemblies in the following way (we'll provide more details on that further on): ``` // TODO If you have planned to use Code Actions functionality that required references // to external assemblies you have to register them here //runtime.RegisterAssemblyForCodeActions(Assembly.GetAssembly(typeof(SomeTypeFromMyAssembly))); ``` Launch the runtime: ``` // starts the WorkflowRuntime // TODO If you have planned use Timers the best way to start WorkflowRuntime is somewhere // outside of this function in Global.asax for example runtime.Start(); ``` The runtime starts and is ready to work, but there is a trick with Timers here. **WorkflowRuntime** will initialize and start when called for the first time. For example, when it gets a list of commands for a process. If your server has been inactive for a while, the system might have unexecuted and expired Timers. When you call `runtime.Start()`, the Timers will be executed. To speed up this process, delete `runtime.Start()` from the `WorkflowRuntime InitWorkflowRuntime()` function and call it in your app's initialization code. The next section will cover the process of integrating scheme designer into your application. --- # WorkflowRuntimeSettings: Configuration This section describes the fields that might be implemented in the class `WorkflowRuntimeSettings` and also by considering possible pre-execution scenarios to managing workflows through Workflow Engine. ## Settings description[​](#settings "Direct link to Settings description") In the table below has been included the main values that can be used: || Name | Value type | Description | ||:---------------------------------------------|---------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| || ExecutionSearchOrder | ExecutionSearchOrder | The order of the Action, Condition, or Rule which is searched by name. | || IsAutoUpdateSchemeBeforeGetAvailableCommands | Boolean | It enables automatic schema of a process update before getting the commands list if it is allowed in current activity. | || UseUtcDateTimeAsRuntimeTime | Boolean | Runtime use UTC date and time if true is set and Local date and time if false. | | SchemeParsingCulture | CultureInfo | Culture is defined for parsing some text parameters from scheme. Default option can be set to: *'InvariantCulture'*. | | CancellationTokenHandling | CancellationTokenHandling | The way of handling: `System.Threading.CancellationToken`, after activity execution. | | PersistenceProviderQueryRetries | Int32 | The number of retries of a query that is allowed. | | PersistenceProviderQueryRetryDelay | TimeSpan | The delay between retries. | | MaxNumberOfPerformedActivities | Int64 | It is the maximum number of activities that can be executed. It can be used to protect your application from infinite loops. Any value less than 1 means unlimited. Unlimited by default. | | MaxPreExecutionTransitions | Int32 | The maximum number of transitions during pre-execution mode. | | LicenseSemaphoreTimeout | Int32 | It works only for licenses with thread limit. | | ParentProcessIdledWaitTimeout | Int32 | It is defined as the Timeout of waiting for unblocking (status = not Running) of the root process. | This is the `WorkflowRuntimeSettings` class which accepts the described options. This object contains the default values: ``` public WorkflowRuntimeSettings() { ExecutionSearchOrder = ExecutionSearchOrder.LocalGlobalProvider; IsAutoUpdateSchemeBeforeGetAvailableCommands = false; UseUtcDateTimeAsRuntimeTime = false; SchemeParsingCulture = CultureInfo.InvariantCulture; CancellationTokenHandling = CancellationTokenHandling.Ignore; PersistenceProviderQueryRetries = 3; PersistenceProviderQueryRetryDelay = TimeSpan.FromMilliseconds(100); MaxNumberOfPerformedActivities = Int64.MaxValue; MaxPreExecutionTransitions = 100; LicenseSemaphoreTimeout = 10000; ParentProcessIdledWaitTimeout = 30000; }; ``` --- # Migration to Vue 3 ## Overview[​](#overview "Direct link to Overview") Starting with `Workflow Engine 20.0.0` templates are based on `Vue 3` which leads to some code changes. `ElementUI` is replaced by `Element Plus`, which also affects component usage. This upgrade **drops Internet Explorer support**. ## Vue3[​](#vue3 "Direct link to Vue3") Due to `Vue 3` reactivity and template compiler changes, some adjustments are required in template definitions, syntax, and the event system. ### Data shape[​](#data-shape "Direct link to Data shape") In `Vue 3` the **full reactive state shape** should exist on first render. Each template now has a `_Data` factory alongside `_Init`. The data factory must return the entire state with default values. In init methods you have `me.VueConfig.state` — a reactive object holding state. *Before* ``` function customforms_basicsplugin_sendemailform_Init(me) { me.VueConfig.methods.UpdateLanguage = function () { me.VueConfig.data = Object.assign(me.VueConfig.data, { labels: WorkflowDesignerConstants.EditParametersFormlabel, customLabels: WorkflowDesignerConstants.EditParametersFormlabel.CustomForms.SendEmail, ButtonTextSave: WorkflowDesignerConstants.ButtonTextSave, ButtonTextCancel: WorkflowDesignerConstants.ButtonTextCancel, parameterFromProcessColor: WorkflowDesignerConstants.ParameterFromProcessColor, }) } me.VueConfig.methods.UpdateLanguage(); me.VueConfig.data = Object.assign(me.VueConfig.data, { customTitle: undefined, readonly: false, parameterFromProcess: { MailServerPort: false, MailServerSsl: false, IsHTML: false }, FormData: {}, Field: {}, useAuth: false }); } ``` *After* ``` function customforms_basicsplugin_sendemailform_Data(me) { return { customTitle: undefined, readonly: false, parameterFromProcess: { MailServerPort: false, MailServerSsl: false, IsHTML: false }, FormData: {}, Field: { Name: { Name: '', Title: '', Comment: '', Type: '', IsRequired: '', DefaultValue: null }, State: { /* Same structure as Name field */}, MailServer: { /* Same structure as Name field */}, MailServerPort: { /* Same structure as Name field */}, MailServerFrom: { /* Same structure as Name field */}, To: { /* Same structure as Name field */}, CcList: { /* Same structure as Name field */}, BccList: { /* Same structure as Name field */}, ReplyToList: { /* Same structure as Name field */}, Subject: { /* Same structure as Name field */}, MailServerSsl: { /* Same structure as Name field */}, IsHTML: { /* Same structure as Name field */}, MailServerLogin: { /* Same structure as Name field */}, MailServerPass: { /* Same structure as Name field */}, Body: { /* Same structure as Name field */} }, useAuth: false } } function customforms_basicsplugin_sendemailform_Init(me) { const {VueConfig} = me; const {state: data, methods} = VueConfig; methods.UpdateLanguage = function () { Object.assign(data, { labels: WorkflowDesignerConstants.EditParametersFormlabel, customLabels: WorkflowDesignerConstants.EditParametersFormlabel.CustomForms.SendEmail, ButtonTextSave: WorkflowDesignerConstants.ButtonTextSave, ButtonTextCancel: WorkflowDesignerConstants.ButtonTextCancel, parameterFromProcessColor: WorkflowDesignerConstants.ParameterFromProcessColor, }) } methods.UpdateLanguage(); } ``` ### Binding to undefined[​](#binding-to-undefined "Direct link to Binding to undefined") Expressions that resolve to undefined on the first render may cause warnings and break reactivity. Use conditional rendering to ensure the value exists before binding. ``` ``` *After* ``` ``` *Before* ``` ``` *After* ```
``` ### Undefined values in loops[​](#undefined-values-in-loops "Direct link to Undefined values in loops") Looping over an undefined value throws. Split `v-if` and `v-for` so `Vue` won't attempt to iterate when data is missing. For some elements you must move `v-for` to a wrapping `