# Activity An activity is a stable state where a [process instance](https://docs.workflowengine.io/get-started/concepts/process-instance/) waits. Activities are defined in a [scheme](https://docs.workflowengine.io/get-started/concepts/scheme/). Every scheme has exactly one initial activity where new process instances start and can have zero or more activities marked as final. A process instance leaves an activity when a [command](https://docs.workflowengine.io/get-started/concepts/command/) executes, a timer fires, or an automatic [transition](https://docs.workflowengine.io/get-started/concepts/transition/) fires. A process instance is always positioned at exactly one activity at a time. During normal execution, when a process instance enters an activity, the activity can execute useful work through [actions](https://docs.workflowengine.io/get-started/concepts/action/). Actions are blocks of code that run automatically upon entry, such as sending notifications, updating business data, or calling external services. Actions are attached to the activity via the `Implementation` property. ## Activity properties Each activity has a set of properties that define its identity, behavior, and position in the scheme. ### Core properties These are the most commonly configured properties: Table: Core activity properties | Property | Type | Description | |----------------|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| | Name | `string` | Case-sensitive, unique identifier for the activity within the scheme | | State | `string` | The business state name associated with this activity; non-unique or absent | | IsInitial | `bool` | Only one activity per scheme may be initial | | IsFinal | `bool` | Marks a final activity; optional in regular schemes and required in schemes used for inlining | | IsForSetState | `bool` | Marks this activity as the target when moving a process to a state by name. Only one activity per state value should have this enabled | | Implementation | `list` of `ActionDefinitionReference` | Actions that execute when a process instance enters this activity | ### Scenario-specific properties These properties are not always needed. Some are used in specialized scenarios such as disabling database persistence, setting timeouts, or handling errors. Others are metadata helpers: Table: Scenario-specific activity properties | Property | Type | Description | |---------------------------------|---------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------| | IsAutoSchemeUpdate | `bool` | Allows the process instance to be updated to a new scheme version when it reaches this activity | | UserComment | `string` | Optional custom comment attached to the activity | | Annotations | `list` of `Annotation` | Key-value metadata attached to the activity; see Annotations | | DisablePersistState | `bool` | Skips saving the current state of the process instance when entering this activity | | DisablePersistTransitionHistory | `bool` | Skips saving the transition history entry when passing through this activity | | DisablePersistParameters | `bool` | Skips saving the parameter values when entering this activity | | ExecutionTimeout | `ActivityTimeout` | Limits activity execution time and cancels it when the interval expires; asynchronous actions must use the provided `CancellationToken` | | IdleTimeout | `ActivityTimeout` | Limits how long the process instance may sit in this activity without any transition | | ExceptionsHandlers | `list` of `ActivityExceptionsHandler` | Configures what should happen if an action throws an exception | | PreExecutionImplementation | `list` of `ActionDefinitionReference` | Actions executed only during pre-execution simulation | ### Exception handling When an action on an activity throws an exception, you can add exception handlers to the activity. If no handler processes the exception, it reaches the normal workflow error handling. Each handler has two parts: which exceptions to catch and what to do when one occurs. The runtime evaluates handlers by ascending `Order` and uses the first matching handler. To catch a specific exception, use its full type name such as `System.InvalidOperationException`. To catch any exception, use `*`, and place that handler after more specific handlers. Four actions are available when an exception is caught: - **SetActivity** - move the process instance to another activity. You specify which activity by name. - **SetState** - move the process instance to a business state. The runtime finds the activity that matches that state. - **Retry** - run the activity implementation again. You specify how many times to retry before giving up. - **Ignore** - suppress the exception and continue workflow execution. ```xml ``` In this example, `InvalidOperationException` reaches the handler with `Order="1"`, so the process moves to the `Failed` activity. Any other exception reaches the wildcard handler with `Order="2"` and retries the activity implementation up to three times. ### Execution and idle timeouts An activity can have two timeouts that limit how long things can take. - **ExecutionTimeout** - requests cancellation when activity execution exceeds the configured interval, then applies the configured result. - **IdleTimeout** - limits how long the process instance can sit at this activity without any transition firing. If the timer expires, the runtime takes the configured action. `ExecutionTimeout` cancels execution through the `CancellationToken` passed to asynchronous actions and custom activity execution. It cannot stop synchronous code or asynchronous code that ignores the token; the runtime waits for that code to finish before handling the timeout. Each timeout defines an interval and what to do when it expires. `ExecutionTimeout` supports `SetActivity`, `SetState`, and `Retry`. `IdleTimeout` supports `SetActivity` and `SetState`. `IdleTimeout` is implemented as an interval timer and supports [work calendars](https://docs.workflowengine.io/get-started/concepts/work-calendar/) when its value uses calendar-aware units such as `2wd` or `4 whours`. `ExecutionTimeout` parses a regular elapsed-time interval directly and does not use work calendars. ```xml ``` In this example, if the process stays at the `Review` activity for more than 2 days without advancing, the runtime moves it to the `Escalated` state. ### Disabling persistence By default, the runtime saves the process state, its parameters, and the transition history to the database through the [persistence](https://docs.workflowengine.io/get-started/concepts/persistence/) provider every time the process moves. For high-throughput scenarios where every millisecond counts, you can skip some of these saves on a per-activity basis: - **`DisablePersistState`** - skip saving the current state (activity and status) for this activity. - **`DisablePersistParameters`** - skip saving the parameter values for this activity. - **`DisablePersistTransitionHistory`** - skip saving the transition history entry when moving through this activity. These flags do not affect reads - the runtime still loads state and parameters from the database when needed. They only disable the write that happens after each transition. Use them on high-traffic activities where you are willing to lose the latest state data in exchange for faster execution. ### Pre-execution implementation An activity can have two sets of actions: the regular `Implementation` that runs during normal execution and `PreExecutionImplementation` that runs only during pre-execution simulation. During simulation, the runtime uses `PreExecutionImplementation` instead of `Implementation` for a visited activity. Pre-execution actions should implement simulation-specific behavior. They can still call external systems, so adding an action to `PreExecutionImplementation` does not make that action free of side effects. ```xml ``` ### What is State The `State` property represents the **business state**, which is the human-readable status such as "Draft", "Under Review", "Approved". An `Activity` is the **physical state** that the runtime tracks internally. One `State` can be shared across multiple activities; for example, "Under Review" can be the state for both a `ManagerReview` activity and a `DirectorReview` activity. ## Declaring activities in a scheme Activities are listed in the `` section of a scheme. Each `` element defines one waiting point in the process. Activities are connected by [transitions](https://docs.workflowengine.io/get-started/concepts/transition/), which determine the paths a process instance can take from one activity to another. The activity itself does not execute logic. It is a named waiting point that holds the process instance until an outgoing transition fires. Actions run when the process instance arrives at an activity. Each activity can be configured to perform specific work, such as sending notifications, changing data, or calling an external service. This work is defined by the activity's [actions](https://docs.workflowengine.io/get-started/concepts/action/). ```xml ``` `IsInitial="true"` marks the activity where Workflow Engine positions new instances. `IsFinal="true"` is a marker: if execution stops in that activity, the runtime sets the process status to `Finalized`. The marker does not prohibit outgoing transitions. A regular scheme may have no final activities, but a scheme used for inlining must have at least one so the inlined fragment can connect back to its parent scheme. ## Managing activities from code You can move a process instance to a specific activity or state programmatically without going through a normal transition. These methods are useful for error recovery, manual overrides, and testing. ### SetState method `SetStateAsync` selects an activity whose `State` matches the requested name and whose `IsForSetState` property is `true`. By default, the method executes that activity's `Implementation` and follows its automatic transitions, bypassing normal incoming transition conditions: ```csharp var setStateParams = new SetStateParams(processId, "Approved"); await runtime.SetStateAsync(setStateParams); ``` ### SetActivity method Moves the process to a specific activity directly, without requiring its `State` or `IsForSetState` properties. You have two options: - `SetActivityWithExecutionAsync` - moves and runs the activity's actions. - `SetActivityWithoutExecutionAsync` - moves without running actions. ```csharp var scheme = processInstance.ProcessScheme; var activity = scheme.FindActivity("Review"); await runtime.SetActivityWithExecutionAsync( "user-42", null, null, activity, processInstance); ``` ### Resume process `ResumeAsync` continues from the current activity, or from the activity named in `ResumeParams`, without executing that starting activity's `Implementation` again. It evaluates the activity's outgoing automatic transitions and follows one when its conditions pass. Unlike `SetStateAsync`, `ResumeAsync` does not select a target by business state or execute the selected starting activity: ```csharp var result = await runtime.ResumeAsync(processId); ``` ### Finding activities in a scheme Every process instance holds a reference to its scheme. Use it to look up activities by name or find the starting point: ```csharp var scheme = processInstance.ProcessScheme; // Find the initial activity var start = scheme.InitialActivity; // Find a specific activity by name var review = scheme.FindActivity("Review"); // List all activities List allActivities = scheme.Activities; ``` `FindActivity` throws an exception if no activity with that name exists. ## See also - [Transition](https://docs.workflowengine.io/get-started/concepts/transition/): Transitions connect activities and define what moves a process instance forward. - [Command](https://docs.workflowengine.io/get-started/concepts/command/): Commands trigger transitions from an activity. - [Scheme](https://docs.workflowengine.io/get-started/concepts/scheme/): A scheme organizes activities and transitions into a process definition. - [Process Instance](https://docs.workflowengine.io/get-started/concepts/process-instance/): A process instance moves through activities and tracks its current position. ## Frequently asked questions **What is an activity in Workflow Engine?** An activity is a named stable state in a scheme where a process instance waits until a command, timer, or automatic transition moves it forward. Every scheme has exactly one initial activity and may have zero or more activities marked as final. **Can multiple activities share the same state?** Yes. Multiple activities can have the same `State` value. For example, both a `ManagerReview` and a `DirectorReview` activity can use the "Under Review" state. The activity name identifies the physical waiting point, while the state is the business status. **Where are activities declared in a scheme?** Activities are declared in the `` section of the scheme XML. Each `` element defines one waiting point, and activities are connected by transitions that determine how a process instance moves from one to another.