Execution Model
Release-backed guide to Elsa's execution model in 3.8.0, covering direct execution, dispatched execution, triggers, bookmarks, stimuli, persistence, and recovery.
This guide explains how Elsa 3.8.0 starts workflows, pauses them, resumes them, and persists their state. It is intended for both developers wiring Elsa into an application and Studio users who need a reliable mental model for what happens after a workflow is published or executed.
If you want the broad platform picture first, read the Architecture overview. If you need activity-level waiting and resume patterns, read Long-Running Workflows.
The four execution paths
Elsa uses four closely related runtime paths:
Direct execution
IWorkflowRunner
Runs a workflow in-process immediately
tests, simple in-process execution, custom host code
Runtime client
IWorkflowRuntime
Creates workflow clients that start, run, and resume persisted instances
most application code
Background dispatch
IWorkflowDispatcher
Queues definition, instance, trigger, and resume work for background execution
server-side and distributed execution
Stimulus delivery
IStimulusSender / IStimulusDispatcher
Matches external stimuli to triggers and bookmarks
HTTP, timers, messages, events, callbacks
In practice:
IWorkflowRunneris the low-level "run now" path.IWorkflowRuntimeis the higher-level operational API.IWorkflowDispatcheris the asynchronous queueing boundary.stimuli are how external events start new instances or resume waiting ones.
Start, run, pause, resume
The normal lifecycle in 3.8.0 looks like this:
A workflow definition is published.
Elsa indexes startable trigger activities from the published definition.
A workflow is started directly, dispatched, or matched by a trigger.
Activities execute until the workflow finishes or an activity creates a bookmark.
The commit-state handler persists bookmarks, variables, execution logs, and workflow state.
Later, a matching stimulus resumes the workflow from the stored bookmark.
This cycle can repeat many times for one workflow instance.
Execute versus dispatch
Execute
IWorkflowRunner builds a WorkflowExecutionContext, schedules the workflow, and runs it in the current process. It is the most direct path and does not queue work.
Use direct execution when:
you are writing tests
you need an immediate result in the same process
you are running a workflow without background infrastructure
Dispatch
IWorkflowDispatcher is the queueing abstraction. In 3.8.0, it supports four request types:
DispatchWorkflowDefinitionRequestto start a new instance from a definitionDispatchWorkflowInstanceRequestto continue an existing instanceDispatchTriggerWorkflowsRequestto start workflows from a trigger stimulusDispatchResumeWorkflowsRequestto resume workflows waiting on bookmarks
The default BackgroundWorkflowDispatcher sends these requests to background command handling. When dispatch happens during workflow execution and transactional outbox support is enabled, TransactionalWorkflowDispatcher writes the work to Elsa's workflow dispatch outbox first, then hands it off after state commit.
Use dispatch when:
the caller should return before workflow work completes
you need queue-based orchestration that can be combined with persistent runtime storage and distributed hosting
child workflow execution should not be tied to the current request lifetime
For lower-level dispatch details, see Workflow Dispatcher Architecture.
Triggers, bookmarks, and stimuli
These three concepts are the core of Elsa's event-driven model.
Triggers start new workflow instances
In 3.8.0, Elsa indexes triggers from published workflow definitions using TriggerIndexer. Two conditions matter:
the activity must implement
ITriggerthe activity must be marked
CanStartWorkflow
That means not every blocking activity automatically becomes a start trigger. For example, timer-style activities only start new workflow instances when the workflow designer or code marks them as startable.
The stored trigger record contains the workflow definition/version IDs, the activity ID, a trigger name, an optional payload, and a deterministic hash.
For Studio users, this is the practical rule:
publishing a workflow makes Elsa index its start triggers
unpublishing or changing the workflow causes trigger reindexing
if a workflow does not start from an expected trigger, first confirm the activity is configured as a start trigger
Bookmarks pause existing workflow instances
Bookmarks belong to workflow instances, not workflow definitions. Activities create them through ActivityExecutionContext.CreateBookmark(...).
When a bookmark is created, Elsa:
captures the bookmark name, payload, hash, activity ID, activity node ID, and activity instance ID
adds the bookmark to the execution context
persists it during state commit
CreateBookmark(...) hashes the bookmark using the bookmark name, the payload, and optionally the activity instance ID. That is why resume payload shape must match what the activity originally stored.
Use bookmarks when a workflow instance should wait for:
an HTTP callback or approval link
an event or signal
a timer or scheduled wake-up
a message from another system
a background task completion signal
Stimuli either start or resume work
Stimuli are the external inputs Elsa tries to match against stored triggers and bookmarks.
In 3.8.0, StimulusSender first tries to start new workflows when the stimulus is not scoped to a specific existing instance or activity. It then tries to resume matching bookmarks.
That means one incoming event can do both:
start new instances from trigger definitions
resume existing suspended instances
If no bookmark matches, Elsa enqueues the stimulus in the bookmark queue as a reliability measure. The recurring TriggerBookmarkQueueRecurringTask keeps signaling the queue worker so recently created bookmarks can still pick up stimuli that arrived slightly too early.
What gets persisted
In 3.8.0, bookmark persistence is no longer handled by the old bookmark middleware. DefaultCommitStateHandler now commits runtime state.
On commit, Elsa persists:
bookmark changes
activity execution logs
workflow execution logs
persisted variables
the workflow instance state itself
After saving state, Elsa emits WorkflowStateCommitted, which is also the hook used to process deferred dispatch outbox work.
For operators, the key implication is simple: if you want workflows to survive restarts, resumes, and delayed callbacks, configure runtime persistence rather than relying on in-memory-only execution.
How resume matching works
When Elsa resumes bookmarks, WorkflowResumer:
builds a bookmark filter from bookmark ID or from hashed stimulus data
acquires a distributed lock for that filter
loads matching bookmarks
creates a workflow client for each workflow instance
runs the instance from the matched bookmark
This lock is important in clustered deployments because it reduces duplicate resume attempts across nodes.
How to choose the right model
Run a workflow immediately in process
IWorkflowRunner
simplest path, no queue boundary
Start or resume managed persisted workflows from application code
IWorkflowRuntime client API
higher-level operational API
Queue work for background execution
IWorkflowDispatcher
decouples execution from caller lifetime
React to external events
trigger plus stimulus delivery
starts new instances automatically
Pause and continue later
bookmark-based activity
persists wait state and resumes on matching stimulus
Practical examples
Start immediately in code
Queue a new workflow instance
Resume workflows waiting on a known stimulus
The activityTypeName must match the bookmark-producing activity type or bookmark name used when the workflow paused.
Common mistakes
Treating every blocking activity as a start trigger. In
3.8.0, start triggers must be bothITriggeractivities and markedCanStartWorkflow.Using in-memory-only runtime services for workflows that must survive process restarts.
Sending a resume payload that does not match the original bookmark payload shape, causing the hash lookup to miss.
Assuming dispatch means "already executed". Dispatch only means the request has been queued successfully.
Expecting an unpublished workflow definition to receive trigger traffic.
Related guides
Last updated