Elsa Workflows vs Workflow Engine NEO

Compare Elsa Workflows 3.7 with Workflow Engine NEO across process modeling, human tasks, multitenancy, operations, and cost, read against Elsa 3.7 and NEO v22.

Rylee SollPublished36 minutes

Senior backend developer and Workflow Engine NEO product team lead


I began this comparison with a practical question: if I were choosing a workflow engine for a product that my team would operate for years, which responsibilities would each engine take over, what would we still have to build, and which model would be easier to live with? My short answer is that Workflow Engine NEO usually fits products centered on business state, human decisions, forms, and tenant isolation, while Elsa Workflows 3.7 usually fits products centered on technical orchestration, events, integrations, and code-first activity graphs.

A production workflow waits for users and external systems, survives restarts, fires timers, resumes in the right place, and continues as definitions change. When people participate, the product also needs assignments, permissions, forms, deadlines, and a history that explains each decision.

Engine selection itself is an architecture decision. The engine determines where execution state lives, how work resumes after a wait, who owns timers, what can run safely on several nodes, and how much responsibility remains in application code. SaaS makes this boundary especially visible: the same workflow may run for thousands of objects across tenants, while identity, data, jobs, forms, and audit must stay isolated together.

Once adopted, a workflow engine normally remains part of the product for years. I consider replacing it after release a last resort: live processes, authorization rules, background work, decision history, and integrations already depend on it, and upgrades must happen while the system continues to serve users. The cost and migration risk can make that replacement impractical for a mature product.

The comparison follows responsibilities rather than feature catalogs. I use the same question throughout the article: what does the engine provide, what must my team design, implement, and operate around it, and which model can we maintain safely for years? The remaining sections build the full answer from the process model, application code, storage, and operational behavior.

I compare Workflow Engine NEO with Elsa 3.7. I do not anchor the Workflow Engine side to one release: its release history and public compatibility policy extend back to 2014. I use human-centric SaaS as a demanding test case because it brings multitenancy, forms, participant permissions, and updates to live processes into the same system. Those concerns affect the weight I give each criterion, but they do not limit the comparison to SaaS.

For the concrete example, I use a document approval process. An employee submits a document, and a manager can approve or reject it. If the manager misses the deadline, the process escalates to a senior manager. I intentionally leave the senior manager's next actions out of the example so they do not obscure the comparison. I also look separately at multitenancy, form integration, and AI agents in two roles: an agent can make a decision as a process participant, or the workflow can invoke and coordinate agents as part of its work.

What each product includes

The diagram below separates the components supplied by each workflow product from the responsibilities that remain with the host application.

The components of Workflow Engine NEO and Elsa Workflows, with application-owned responsibilities shown separately

Workflow Engine NEO

Workflow Engine NEO combines an embeddable Workflow Runtime with persistence. Its separately adoptable product features include Workflow Designer, the Workflow Engine HTTP API, Inbox, and the Forms Plugin. The application keeps its identity model, domain services, and business data. Actions call those services, while Rules check identities for command permissions.

Elsa Workflows

Elsa combines an activity-graph runtime with Management and Runtime stores, dispatching, and scheduling. Elsa Server and Elsa Studio can be added for API hosting, visual authoring, and management screens. A minimal code-first application does not need every optional component in the hosting model. The host application or an existing task platform still owns human-task assignment, permissions, Inbox, forms, and business audit. Elsa custom activities connect the workflow to domain services.

Short answer

The practical choice depends on what drives most of the product's complexity and changes most often: business state and decision authority, or technical orchestration.

Overall recommendation

Workflow Engine NEO:

Workflow Engine NEO treats a process as the lifecycle of a business object. Workflow Runtime stores the document's current Activity and State and returns the Commands available at that point. To check permissions, the application represents each user or agent with an Identity; Workflow Runtime evaluates that Identity against Actors, Rules, and Restrictions. In products built around forms and approvals, this allows the team to model the workflow with business concepts such as States, Commands, and participant permissions. The same tenant context scopes HTTP API requests, workflow instances, timers, history, Inbox items, and Forms Plugin data.

Elsa:

Elsa Workflows treats a workflow as a graph of executable activities: what to run, which event to wait for, and which path to schedule next. Its execution model covers direct execution, persistent runtime execution, background dispatch, and resumption through triggers and bookmarks. This model fits integration-heavy processes well, particularly when a team wants typed code-first workflows and already owns its task, forms, identity, and tenant platform.

Criteria at a glance

Table: Elsa Workflows 3.7 and Workflow Engine NEO decision summary

AreaWorkflow Engine NEOElsa Workflows
How the workflow models differWorkflow Engine NEO models a business object's lifecycle through Activities, States, and outgoing Command or timer Transitions.Elsa Workflows models execution as an activity graph with branches, waits, and bookmark-based resumption.
One approval process in both enginesWorkflow Engine NEO covers process execution, available decisions, command authorization, Inbox, Forms Plugin, and approval history.Elsa Workflows covers graph execution and durable waits; an external task layer owns assignment, permissions, Inbox, forms, and business audit.
AI agents in workflowsIdentity, Actors, Rules, and Restrictions control which Commands people and AI agents can execute.Elsa Agents optionally adds Activities for model and tool calls.
Visual workflow authoring and editor embeddingWorkflow Designer embeds in JavaScript, React, Angular, and Blazor with full-node control.Elsa Studio loads Blazor WebAssembly outside Blazor, making those pages heavier.
Code-first workflow authoringCode and Workflow Designer edit stored Schemes; rebuilds must preserve visual changes.WorkflowBase and Elsa Studio drafts remain separate; teams must choose the authoritative source.
Composition and child workflowsInline Activities reuse Schemes; Subprocesses form a managed process tree.Definitions provide inline reuse; child workflows run directly, in the background, or in bulk.
Long-running waitsIdled instances need no thread or per-process memory; TimerManager polls due Timers in batches.Stored bookmarks outlive the execution context; multi-node scheduling adds Quartz.NET or Hangfire with shared storage.
Deadline control, reminders, and escalationTimer-triggered Transitions model reminders and deadlines; target Actions send notices.An SLA sequence adds time events; external task code closes, expires, or reassigns tasks.
Concurrent events and process lockingBuilt-in persistence locks serialize Process Instance execution and due Timers across nodes.Multi-node resumes need Distributed Runtime and shared locking; external task updates must be atomic.
Scheme versions and live process updatesRunning instances keep stored Scheme versions; updates are explicit or lazy at marked Activities.Publishing sets the default for new starts; alteration plans migrate selected running instances.
Administrative changes to running processesWorkflow Runtime offers direct State, Activity, Parameter, and resume operations for individual Process Instances.Elsa Alteration plans apply activity and variable changes across filtered instance sets.
Multitenancy architectureTenant routing covers four placement modes and scopes the HTTP API, workflow data, Timers, Inbox, Forms Plugin, approvals, and permissions.Management and Runtime stores, Elsa Identity, and configured scheduler jobs are tenant-aware; the team isolates external tasks and forms.
Onboarding complexityNEO uses fewer concepts for approvals.Elsa Workflows requires more execution and infrastructure concepts: resumption, dispatch, scheduling, and persistence.
Operational diagnosticsTransition History explains State changes by route, trigger, actor, executor, and time.Elsa's workflow journal records the technical flow; the application supplies the business decision audit.
Embedding and architectural impactWorkflow Runtime embeds in existing services; its UI and API integrations remain optional.Durable background execution and visual operations add stores, scheduling, dispatch, APIs, and Studio.
Licensing and total costNEO's price covers Forms Plugin, Inbox, tenant routing, Timers, and multi-server coordination.Elsa Core has no runtime fee; total cost includes task/forms code, multitenancy, distributed operations, and optional paid support.

How the workflow models differ

Why it matters

The engine model determines how a team reads and changes a process. Each notation exposes a different set of concepts and connections for the same business route.

The example route starts when an employee submits a document. The manager chooses Approve or Reject; a missed deadline escalates the process to a senior manager. The senior manager's later actions are omitted. The top of the diagram shows the common business route. The lower panels express it in each engine's notation.

The same approval process modeled in Workflow Engine NEO and Elsa Workflows

Workflow Engine NEO:

The process contains Activities named DraftActivity, ManagerReview, Approved, Rejected, and Escalated. Each Activity can have a business-facing State; for example, ManagerReview maps to Under review. A Command named Submit moves the process from DraftActivity to ManagerReview. The Approve and Reject Command-triggered Transitions leave ManagerReview.

Both decisions are restricted to the manager. Each restriction on those Transitions refers to the Manager Actor. Workflow Runtime resolves its Rule, which checks the user's Identity. A Timer triggers an outgoing Transition at the approval deadline. Two restricted Command-triggered Transitions and one Timer-triggered Transition together leave ManagerReview.

Elsa:

After SubmitDocument completes, a Fork configured with WaitAny schedules two branches. In one branch, RunTask creates a bookmark and waits for the manager's result. FlowDecision evaluates that result and routes execution to application activities that record approval or rejection. In the other branch, a Timer or Delay waits for the deadline; after completion, the graph schedules the escalation activity. When one branch completes, Elsa Workflows completes Fork(WaitAny), continues execution, and cancels the still-running branch in its parallel execution model.

The product team records the document's status in domain data or through explicit activities; the control-flow activities represent execution rather than domain status.

One approval process in both engines

Why it matters

The diagram shows the workflow, but leaves the integration work off the page. The product must list available decisions, verify the manager's permission, provide the task and form, accept the answer, and handle the deadline. Responsibilities outside the engine become code or infrastructure for the product team.

Application integration for the approval process in both engines

Workflow Engine NEO:

The employee and manager use the product's existing UI. Through Workflow Runtime or the Workflow Engine HTTP API, the application lists available Commands and sends Submit, Approve, or Reject. Workflow Runtime evaluates Restrictions with the Rule Provider to decide which Commands the manager sees. When requested, it repeats the check as the manager submits a decision. By default, Workflow Runtime persists Activity, State, changed persistent parameters, Timer registrations, and Transition History.

Workflow Engine NEO provides Inbox, Forms Plugin, and approval history for the product UI. Approval logic connects through an Action Provider, which lets the workflow call product services, and a Rule Provider, which evaluates whether an Identity matches the Rules referenced by Transition Restrictions.

The document remains in the application's database; persistence stores process state. When the deadline Timer becomes due, TimerManager evaluates the escalation transition. When Conditions pass, the transition changes State and its Action sends the notification. A multi-node deployment keeps TimerManager and persistence unchanged; no other component is added or operated in production.

Elsa:

The application starts the workflow through Elsa Workflows' Runtime API. The external application interaction guide documents the division: RunTask creates a Task ID and bookmark, sends a request to an application handler, and suspends until completion. A parallel Timer creates a deadline bookmark; the scheduler registers its resume time.

On one node, Elsa Workflows' LocalScheduler keeps timer work in memory and rebuilds schedules from persisted bookmarks after restart. A multi-node deployment requires Distributed Runtime and distributed locking. Scheduled work can use clustered Quartz.NET or Hangfire backed by durable shared storage. Your team operates and monitors the scheduler and its shared storage. The Management store holds definitions, instances, workflow state, and incidents; the Runtime store holds bookmarks, triggers, and execution records.

The product team must build manager assignment, task permissions, Inbox, forms, and business decision history in its task/forms layer or connect an existing task platform. After validating the manager, that layer sends the Task ID and result to Elsa Workflows' task-completion endpoint, resuming RunTask. If the deadline wins, the task layer closes or expires the task while a custom activity writes the escalation to domain data.

AI agents in workflows

Why it matters

AI agents now enter business processes in two distinct roles. An agent may make a decision alongside people, where authority and audit matter most. A workflow may also invoke and coordinate one or more agents, where model and tool integration matter more.

Workflow Engine NEO:

The host represents a decision-making agent with an Identity. An adapter calls the model and maps its response to a Command returned by Workflow Runtime for that Identity. Workflow Runtime evaluates Actors, Rules, and Restrictions; the Rule Provider supplies the Rule implementation. A Scheme can expose the ProposeApproval Command to the agent and reserve the Approve Command for managers.

Model calls can run as an Action or custom Activity. The Scheme coordinates them through Transitions, Timers, and Conditions; your team supplies model and tool integrations.

Elsa:

An external agent can return a result for a RunTask through the task layer. Elsa Workflows matches its Task ID to a stored bookmark and resumes the workflow. The task layer authenticates the agent, limits its decisions, applies human-confirmation policy, and writes the business audit.

Elsa Agents is an optional extension that turns configured agents into activities and selected skills into model-callable functions. Its activities compose with other steps and bookmark-based waits but do not persist internal chat history.

For both engines:

Execution journals are not complete AI decision records. Store model version, prompt or instructions, context, tool calls, and result in the product's business audit.

Visual workflow authoring and editor embedding

Why it matters

Embedding determines how an editor fits the frontend, authentication, design system, page weight, and product-specific blocks.

Workflow Engine NEO:

The Workflow Designer edits the Scheme that Workflow Runtime executes. It is a standalone JavaScript component for plain JavaScript, with React and Angular wrappers, plus Blazor interop.

Its API validates Schemes and supports read-only mode, localization, and autocomplete. A Custom Activity Type supplies execution code, parameters, a configuration form, and a complete SVG node template. The Designer lists Actions from the Action Provider and permission Rules from the Rule Provider.

Elsa:

Elsa Studio is a modular Blazor application. Its custom-elements host embeds the editor, definition and instance lists, and instance viewer; its React wrapper uses those elements, which load the Blazor WebAssembly runtime.

Custom activities use typed inputs and outputs. Studio supports custom input components and custom icons, including SVG; its documented extensions do not include complete per-activity canvas-node replacement.

Code-first workflow authoring

Why it matters

Code-first and visually authored definitions can be versioned and tested. Developers should be able to read and change them without reconstructing runtime behavior in their heads. Code and visual editing may share one executable definition or create separate sources of truth.

Workflow Engine NEO example

Workflow Engine NEO:

ProcessDefinitionBuilder defines Activities and their States, Commands, Actors, timers, and Transitions through its fluent API. .Ref(out ...) captures typed Activity and Command objects so Transitions can reuse the references. The ProcessDefinition is the model used by Workflow Runtime and Workflow Designer.

Names handled by the registered Action Provider and Rule Provider remain string contracts. The compiler cannot detect a mismatched Scheme reference, so teams should validate those names in integration tests.

The approval Scheme:

csharp
using System;
using OptimaJet.Workflow.Core.Model;
using OptimaJet.Workflow.Core.Model.Builder;

var scheme = ProcessDefinitionBuilder.Create("DocumentApproval")
    .CreateActor("Employee", "IsEmployee").Ref(out var employee)
    .CreateActor("Manager", "IsManager").Ref(out var manager)
    .CreateCommand("Submit").Ref(out var submit)
    .CreateCommand("Approve").Ref(out var approve)
    .CreateCommand("Reject").Ref(out var reject)
    .CreateTimer("Deadline")
        .Interval(TimeSpan.FromDays(3)).Ref(out var deadline)
    .CreateActivity("Draft").Initial().State("Draft").Ref(out var draft)
    .CreateActivity("ManagerReview")
        .State("UnderReview").Ref(out var review)
    .CreateActivity("Approved").Final()
        .State("Approved").Ref(out var approved)
    .CreateActivity("Rejected").Final()
        .State("Rejected").Ref(out var rejected)
    .CreateActivity("Escalated").Final()
        .State("Escalated").Ref(out var escalated)
    .CreateTransition("Submit", draft, review)
        .TriggeredByCommand(submit)
        .CreateRestriction(employee, RestrictionType.Allow)
    .CreateTransition("Approve", review, approved)
        .TriggeredByCommand(approve)
        .CreateRestriction(manager, RestrictionType.Allow)
    .CreateTransition("Reject", review, rejected)
        .TriggeredByCommand(reject)
        .CreateRestriction(manager, RestrictionType.Allow)
    .CreateTransition("Escalate", review, escalated)
        .TriggeredByTimer(deadline)
    .ProcessDefinition;

The example assumes a registered Rule Provider supplies IsEmployee and IsManager. Actions, forms, and the senior manager's route are omitted.

The resulting ProcessDefinition can be saved as a Scheme. Workflow Designer can reopen that Scheme, and code can load it as a ProcessDefinition. Workflow Designer updates the stored Scheme, not fluent C# calls. Teams using both paths should modify the saved Scheme or account for visual changes when rebuilding it from code; otherwise, the rebuild will overwrite them.

Elsa code-first example

Elsa:

An Elsa code-first workflow inherits from WorkflowBase and composes activities. Input<T>, Output<T>, and Variable<T> retain declared .NET types. It combines RunTask, review and deadline branches, and decision routing.

csharp
using Elsa.Scheduling.Activities;
using Elsa.Workflows;
using Elsa.Workflows.Activities;
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime.Activities;
using WorkflowTimer = Elsa.Scheduling.Activities.Timer;

public sealed class DocumentApproval : WorkflowBase
{
    protected override void Build(IWorkflowBuilder workflow)
    {
        var decision = workflow.WithVariable<object>("Decision");

        var review = new Sequence
        {
            Activities =
            {
                new RunTask("ManagerReview")
                {
                    Result = new Output<object>(decision)
                },
                new If(context =>
                    Equals(decision.Get(context), "Approve"))
                {
                    Then = new RecordApproval(),
                    Else = new RecordRejection()
                }
            }
        };

        var sla = new Sequence
        {
            Activities =
            {
                new WorkflowTimer(TimeSpan.FromDays(3)),
                new EscalateToSeniorManager()
            }
        };

        workflow.Root = new Sequence
        {
            Activities =
            {
                new SubmitDocument(),
                new Fork
                {
                    JoinMode = ForkJoinMode.WaitAny,
                    Branches = { review, sla }
                }
            }
        };
    }
}

Application activities represent domain updates, decision history, and escalation. Teams supply an external task/forms layer. It assigns the manager, presents Inbox and forms, checks permission, reports the RunTask result, and closes or replaces user tasks.

Studio can display a workflow registered in an Elsa Server host. It saves edits as a persisted draft without changing WorkflowBase; a provider reload can overwrite that draft. Teams must choose C# or Studio as the source of truth.

Authoring tradeoffs

For both engines:

The snippets contain the workflow definitions. Completing the Workflow Engine NEO example requires its Rule Provider implementation, Actions, and forms; completing the Elsa example requires an external task/forms layer and four application-activity implementations.

Process composition and child workflows

Why it matters

Composition keeps workflows understandable. Distinguish inline workflow reuse from child workflows with distinct state, history, and version.

Workflow Engine NEO:

An Inline Activity references a Scheme. At build time, the pipeline expands it into the parent Scheme with parameter mappings.

Subprocesses are separate Process Instances in a managed tree. Fork Transitions govern lifecycle, parameter transfer, and parent-state control. Runtime APIs retrieve the tree and include child Commands in a root-process query.

For fan-out (one Subprocess per item), the Loops Plugin iterates Parameter values. A ForkStart Transition creates each child in the loop; the parent waits until a Condition confirms all have finished.

An Action can start an ordinary independent process through WorkflowRuntime, read its state, list its commands, and execute them. The application stores the other ProcessId and defines the relationship, waiting behavior, and joint lifecycle.

Elsa:

A published definition marked Usable as activity is reusable inside the parent instance with inputs, outputs, and outcomes. ExecuteWorkflow and DispatchWorkflow create a child directly or by background dispatch, pass input, and can await its result. BulkDispatchWorkflows supports fan-out with one child per collection item.

Independent workflows can start through the dispatcher or Runtime API. The application records their relationship and queries status by instance or correlation ID; Elsa manages triggers and bookmarks. Its instance API does not list available named business decisions and execute one as a Command.

Long-running waits

Why it matters

A process may wait for months. Supporting thousands requires releasing worker threads and in-memory contexts, surviving restarts, and resuming instances with their state and deadlines intact.

Workflow Engine NEO:

Workflow Engine NEO stores a durable execution checkpoint: current Activity and State, Process Status, persistent Parameters, recorded Transition History, and Timer registrations. An Idled instance uses no execution thread or per-process application memory. Workflow Runtime reloads it when a Command, Timer, or API operation starts the next Transition.

TimerManager polls due Timers in batches rather than keeping an in-memory job per wait. In multi-server mode, runtimes take turns processing Timer batches from the shared database. If one node fails, another runtime continues Timer processing. Timers need no recovery; only Process Instances left Running enter recovery. No external scheduler or coordination service is required.

Elsa:

With persistent runtime storage, Elsa persists workflow state and bookmarks. Between bursts, its WorkflowExecutionContext is absent from memory; a stimulus reloads state and resumes the instance. Memory stores lose this state on restart and cannot support durable waits.

The default local scheduler creates one in-memory task per inline Timer or Delay bookmark, with the workflow ID, bookmark ID, and absolute resume time. Memory use grows with scheduled waits. After restart, Elsa recreates tasks for those stored times. Multi-node deployments require Distributed Runtime, distributed locking, and clustered Quartz.NET or Hangfire with durable shared storage.

Deadline control, reminders, and escalation

Why it matters

Approval policies can combine recurring reminders, a final deadline, and escalation. They change workflow structure and application code differently in each product.

Recurring reminders and deadline escalation in both engines

Workflow Engine NEO:

In this Scheme, each reminder or deadline is a Timer-triggered Transition from ManagerReview. The reminder returns to ManagerReview; an Action there can read the executed Transition and send the notification. The deadline moves the process to the Escalated Activity, where an Action sends the escalation notice. Marking that Timer as not overridable preserves its original execution time after the reminder loop. The application supplies these Actions and a Rule Provider for Identity checks during Command execution.

Elsa:

Fork(WaitAny) runs a RunTask manager-decision branch beside a Timer-based SLA branch. The Fork continues when either completes and cancels the unfinished branch. Recurring reminders make the SLA branch wait, send a notification, and wait again until the deadline; a sibling branch would finish WaitAny on the first reminder.

Elsa controls waits and cancellations, not the application-owned human-task record. The external task layer creates and assigns the manager task. On SLA completion, that layer closes or expires the task and creates or reassigns a senior-manager task under application policy.

Concurrent events and process locking

Why it matters

A manager decision and deadline can arrive together. Handlers must not advance the same Process Instance along both routes.

Workflow Engine NEO:

Before a Transition runs, its Command or due Timer locks the Process Instance through persistence and moves its status from Idled to Running. Only one execution for that Process Instance succeeds across nodes sharing a database; any retry is evaluated against the new Activity. If the deadline wins, Approve is invalid. If approval wins, entering Approved clears the deadline Timer registration. Process Instance locking resolves this race; clustering separately locks each due Timer record so only one node executes it.

Elsa:

When one branch completes, Fork(WaitAny) cancels the other. WaitAny does not serialize hosts that loaded the same instance concurrently. Multi-node deployment needs a distributed workflow runtime and shared distributed lock. Cache invalidation and clustered scheduling remain separate components.

The distributed runtime serializes instance execution. The winner completes the Fork, removing the other bookmark before a later request can resume that branch. Canceling RunTask does not update the application-owned task. The external task layer must atomically complete or expire it and reject a late response.

Scheme versions and live process updates

Why it matters

A process can outlive several product releases. A new definition should affect running instances only when the team explicitly migrates them.

Workflow Engine NEO:

Workflow Engine NEO binds each running Process Instance to a stored Scheme version; new instances receive the latest published definition. Its process versioning model lets application code migrate an instance explicitly or lazily at an Activity marked IsAutoSchemeUpdate. This limits lazy migration to named Activities in the business process.

Elsa:

Elsa tracks latest and published definition versions separately. Publishing changes the default version for new REST starts, not existing workflow instances. A Migrate Alteration moves selected instances to a specific version of the same definition; filtered alteration plans provide an operator workflow for bulk changes.

Administrative changes to running processes

Why it matters

Support teams sometimes reposition stuck processes, correct data, or resume execution. Editing workflow data directly is an anti-pattern: it bypasses runtime validation and auditing.

Workflow Engine NEO:

Workflow Engine NEO provides direct state control. It can change a Process Instance's State, move it to a chosen Activity and run or skip its Actions, or resume from the current or selected Activity without executing it again. Operators can correct a persistent Parameter. Because direct State changes bypass Command restrictions, the product team controls access and adds auditing when required.

Applications can invoke these operations in code through Workflow Runtime or through the HTTP API.

Elsa:

The Alterations module can cancel or schedule an activity, modify a variable, or migrate an instance. IAlterationRunner applies immediate changes; its caller then dispatches affected instances. An alteration plan selects instances by ID or filter, creates one job per instance, and records its status and log.

Elsa has no built-in operation that maps a business State to an Activity; repositioning uses activity IDs. Elsa defaults to in-memory plan stores, job stores, and dispatch. Durable plans require persistent stores and a durable dispatcher.

Multitenancy architecture

Why it matters

In a SaaS product, API calls, workflow data, timers, background jobs, Inbox, forms, history, and authorization must use the same tenant context. Shared tables need tenant-aware filters. Dedicated stores need request routing. Scheduled jobs must restore the correct tenant before resuming a workflow. Storage topology affects operations: a separate schema creates a physical boundary inside one database, while a separate database supports independent backup, restore, and data-residency policies. Hybrid routing keeps some tenants in shared storage and places others in dedicated stores.

Tenant storage models and isolation boundaries in both engines

Workflow Engine NEO:

Workflow Engine NEO supports Logical Tenancy in a shared database and schema, Physical Tenancy with a dedicated database or schema, and Hybrid Tenancy that combines them. The multitenancy feature reads the Workflow-Api-Tenant-ID header or a configured default; its registry selects the matching Workflow Runtime and Database Provider. Several logical tenants can share a runtime. A physical tenant can use its own schema or database and a different supported database type.

Tenant scope covers API operations, Schemes and Forms, Process Instances and Parameters, Timers and Transition History, Inbox and Approval History, and permissions. The ASP.NET Core integration applies the request's tenant to these operations and records. Direct Workflow Runtime calls must select the runtime for Physical Tenancy and pass or filter by TenantId for Logical Tenancy.

Elsa:

Elsa's multitenancy model supports shared tables filtered by TenantId and separate databases selected through a connection-string factory. The same factory can send some tenants to shared storage and others to dedicated databases. Elsa has no built-in per-tenant SQL-schema router: EF Core SchemaName is configured for a persistence module when the host registers it.

Tenant context scopes Management and Runtime records, plus users and roles in Elsa Identity. The Quartz.NET and Hangfire integrations add the tenant ID to scheduled jobs and restore the context before workflow execution. The team configures durable scheduler storage, monitors its workers, and includes the job store in backup and recovery.

For the approval process considered in this article, the task/forms layer is external to Elsa. Your application stores TenantId with tasks, forms, assignments, and decision history; filters reads and writes; checks the user's tenant and permission at completion; and restores tenant context before resuming the workflow. Elsa's setup guide configures tenants, tenant resolution, and Elsa Identity. The external task service must enforce its own isolation.

Onboarding complexity

Why it matters

AI tools generate integration code; developers maintain and troubleshoot long-running processes. They must know what the runtime loads and persists, how waits resume, and what happens after restarts, duplicate events, or definition updates.

Workflow Engine NEO:

Workflow Engine NEO's main terms are Scheme, Process Instance, Activity, State, Transition, Command, Action, Actor, Rule, and Timer. Developers must distinguish Activity from State, Command from Action, Actor from Rule, and relate each Process Instance to its stored Scheme version.

For Command or Timer execution, Workflow Runtime loads and locks the Process Instance through persistence. The selected Transition moves it to the target Activity, whose Actions run. A persisted checkpoint can contain Activity, State, Process Status, persistent Parameters, optional Transition History, and Timer registrations. An Idled instance uses no execution thread or per-process application memory; Workflow Runtime reloads it for new work.

Elsa:

Elsa's execution model has a broader vocabulary: workflow graph; Workflow and Activity Execution Contexts; inputs, outputs, variables, and outcomes; bookmarks, stimuli, and triggers; runner, runtime, dispatcher, and scheduler; Management and Runtime stores; incidents; and Alterations.

With runtime persistence, Elsa stores the workflow state and bookmark when an activity pauses for input. A matching stimulus resumes the workflow through that bookmark. Scheduled waits use the scheduler; background workflow requests use the dispatcher.

Operational diagnostics

Why it matters

When a process stalls or produces the wrong result, support must reconstruct its business route and internal execution.

Workflow Engine NEO:

Workflow Engine NEO persists Transition History with route, trigger, actor, executor, and time. Optional Process Logs trace execution and errors; they are in memory by default. Runtime Logs report engine diagnostics. Runtime Events notify subscribers of state changes and errors but store no history. Durable Process Logs need a custom provider.

Elsa:

Elsa's workflow journal, activity execution records, and incidents show its event timeline, Activity state, and faults. Configured OpenTelemetry exports traces and metrics; Log Persistence controls retained inputs, outputs, and internal state.

Embedding and architectural impact

Why it matters

Embedding decides which hosting, storage, authorization, API, and UI boundaries must change and which components the team must secure, update, and monitor.

Workflow Engine NEO:

Workflow Engine NEO embeds Workflow Runtime in a .NET host with a persistence provider. Action Providers call application services; Rule Providers supply application-specific Rule logic evaluated for an Identity. Domain data, identity, API, and UI can remain application-owned. The backend uses Workflow Runtime to create Process Instances and list or execute Commands; it selects and executes their Transitions.

Workflow Engine Web API can publish the Workflow Engine HTTP API from the same ASP.NET Core host or a separate service. Workflow Designer, Approval Plugin Inbox, and Forms Plugin are independent integrations.

Elsa:

Elsa is added to an existing ASP.NET Core host through dependency injection. Code-first workflows can run there without Elsa Server or Elsa Studio.

Durable waits require persistent Management and Runtime stores; background execution uses a dispatcher; scheduled waits add job scheduling. Visual authoring and instance management use the workflow API and Elsa Studio. These parts can share a host. The team still configures and operates the selected stores, workers, scheduler, protected API, and Studio deployment. The hosting guide covers standalone and modular boundaries.

If the product keeps its own API and UI, application code connects Elsa to its identity and domain services, while the product team supplies the external task and forms layer.

Licensing and total cost

Why it matters

Total cost of ownership includes licensing, integration, infrastructure, upgrades, and support.

Workflow Engine NEO:

The editions and prices of Workflow Engine NEO are on the pricing page. Every edition of Workflow Engine NEO includes licensed Forms integration through Forms Plugin. For the approval SaaS considered here, NEO supplies Commands, Rules, Approval Plugin Inbox, tenant routing, Timers, and multi-server coordination. The team configures these capabilities instead of building and operating equivalent application services.

Elsa:

Elsa Core is MIT and has no runtime license fee. Elsa+ lists paid support and professional services from independent providers. Elsa optionally uses MassTransit for broker-backed messaging and workflow dispatch; its distributed-hosting guide uses it for cross-node cache invalidation. The current Elsa integration pins MassTransit 8.5.7 under Apache 2.0. MassTransit v9 requires a commercial subscription scoped by product line or organization. As of September 2026, Elsa has no committed v9 integration or replacement. A MassTransit design must budget for that subscription or the cost of retaining or replacing 8.x.

When to choose Workflow Engine NEO

Choose Workflow Engine NEO when business-object lifecycles are expressed through States, Commands, participant permissions, forms, or deadlines.

When to choose Elsa

Choose Elsa when workflows coordinate events, integrations, background work, parallel branches, or model and tool calls. Approval assignment, permissions, forms, tenant isolation, and audit are outside Elsa, in host code or an external task-and-forms platform.

Other products to evaluate

Hangfire. Hangfire runs background and recurring .NET jobs while the application owns business state and transition rules. Its job queue, retries, and execution dashboard may be enough without a full workflow model.

Quartz.NET. Quartz.NET schedules code by time, calendar, or trigger while the application owns business state. Participant permissions, forms, process versions, and decision history remain outside the scheduler.

Wexflow. Wexflow's visual workflows automate files, scripts, ETL, back-office processes, and operational jobs. Its server, task catalog, and administration UI suit products where system automation dominates human workflow.

Workflow Core. Workflow Core is an embeddable open-source .NET runtime with fluent, JSON, or YAML definitions and pluggable persistence and concurrency providers. Choose it when a code-first runtime matters more than Elsa's Studio and platform services.

Temporal. Temporal coordinates distributed code and workers in several languages through replay-based durable execution. Its Event History replay, deterministic Workflow code, and Worker Versioning differ from both engines.

Durable Functions. Durable Functions provides stateful serverless orchestration on Azure through orchestrator and activity functions, durable timers, and external events. Replay and determinism constraints apply; the choice remains tied to Azure Functions and a configured storage backend.

BPMN platforms. Workflow Engine NEO imports supported BPMN 2.0 elements into an editable Scheme and executes that Scheme. Elsa Workflows 3.7 does not support BPMN import or execution. Dedicated BPMN-native platforms such as Camunda and Flowable use BPMN as the executable workflow definition. Include them in the shortlist when the BPMN model itself must remain the source of truth.

Test both on your own process

Implement the same real process. Build the approval flow in both products using the permissions, integrations, and support operations the production system requires. Include recurring reminders, a final deadline and escalation, and two tenants. Then restart a host during a durable wait, submit a duplicate event, fail an external service call, race a participant decision against the deadline on two nodes, and update the definition while an older instance remains active.

Include the surrounding application work: create a process, show and submit the available decisions, render the form, inspect approval history, and correct workflow state or data. Record the code, configuration, and deployed components required for authorization, tasks and forms, tenant routing, persistence, scheduling, locking, audit, and diagnostics.

Compare recovery, logs, manual repair, and what the team must monitor, back up, and upgrade. Ask a second developer to change one rule and diagnose one failure without help; record the time and concepts they needed.

Map next year's backlog. List changes planned or likely during the next twelve months and map each one to both products. For each product, mark whether the change fits its native concepts or requires a new application component; record infrastructure or operational work separately. Estimate implementation, testing, migration, and support effort for the whole list. The first test measures the current process; the backlog map shows where work will accumulate as the product changes.

Final recommendation

For the long-running, multitenant approval SaaS considered in this article, Workflow Engine NEO is the more practical choice. Elsa fits when technical orchestration drives most changes and the product already owns the human-task capabilities around it. The two tests above show whether that conclusion holds for your product.

Frequently asked questions

How do Workflow Engine NEO and Elsa differ for human approval workflows?
Workflow Engine NEO includes more of the human-task layer: Commands, Rules, Inbox, Forms, timers, and transition history are part of the same product model. Elsa can execute the same approval process, but the application or an existing task platform must own assignment, task permissions, forms, and business audit.
How do Workflow Engine NEO and Elsa isolate tenants in a SaaS product?
Both engines support tenant-aware persistence, and their built-in isolation boundaries differ. Workflow Engine NEO supports shared tables, schema-per-tenant, database-per-tenant, and hybrid routing, and its tenant scope also includes Inbox, Forms, permissions, timers, and process history. Elsa supports shared-database, database-per-tenant, and hybrid database routing for its Management, Runtime, and Identity records, and the team carries tenant context through distributed scheduling and the application task and forms layer.
Can code-first workflows be edited visually in both products?
Both can be opened in a visual editor, and the round trip works differently. Workflow Engine NEO Builder and Designer operate on the same ProcessDefinition, so a scheme can move between code and visual editing, although the Designer does not rewrite the fluent C# source. Elsa Studio can display a WorkflowBase definition, and a visual save creates a persisted definition rather than updating the C# class, so the team chooses one source of truth.
How do both engines resume long waits after a restart?
Both engines resume long waits from persisted data. Workflow Engine NEO stores process state and timers in its database and queries due timers through TimerManager. Elsa stores suspended workflow state and bookmarks; multi-node time scheduling normally adds Quartz.NET or Hangfire with a shared job store.
What should a proof of concept test in both engines before a team chooses one?
Use the same approval flow in both engines, including a deadline, reminder, escalation, restart during a wait, two tenants, a live definition update, and a concurrent decision and timer. Compare the application code outside the definition, the infrastructure, failure diagnosis, and the time a second developer needs to make a safe change.

Test both on your own process

The comparison ends where a proof of concept begins. Model one real approval, with a deadline, an escalation, two tenants, and a restart during a wait, then count the code that lives outside the definition.

Building it yourself is the other option on the table. What the embeddable .NET engine covers shows what you would be rebuilding, and tenant isolation is the section most teams jump to.