workflowengineby Optimajet · since 2014

v22.1.0 · logical, physical, and hybrid tenants · no restart to add one · the tenant-aware HTTP API needs a NEO license

Multi-tenant workflow architecture for .NET SaaS

The second customer is where workflow turns into a tenancy problem. Whose process is this? Which database does it live in? Who is allowed to read it? Workflow Engine by Optimajet answers all three inside your own ASP.NET application. You choose the isolation model per tenant, the engine carries that tenant through every process, timer, and history record, and a new customer is registered while the service keeps running. No separate cluster, no vendor cloud, no second deployment per customer.

One deployment · three tenantshybrid tenancy

Every request names its tenant

acme-corpglobexstark

Workflow-Api-Tenant-ID

One Workflow Engine HTTP API host

IWorkflowTenantLocator resolves the tenant · WorkflowApiPermissions must allow it

Physical tenant · own database

WorkflowRuntime A

serves acme-corp only

db-acme

SQL Server · backed up on its own

Logical tenants · shared database

WorkflowRuntime B

serves globex and stark, every record filtered by TenantId

db-shared

PostgreSQL · one row set per tenant

Both boundaries live in one application and one deployment. Moving a customer from the shared database to a dedicated one is a registration change, not a migration to a different product.

One engine serves every customer,and no tenant sees another tenant's data

Multitenancy lets one Workflow Engine deployment serve many customers or business units from a single application instance, with each tenant seeing only its own data. Without it, every customer needs a deployment of their own, and the cost of the tenth customer is ten times the cost of the first.

Separation works at two levels that you mix freely. Physical tenancy gives a tenant its own database and its own WorkflowRuntime. Logical tenancy puts several tenants on one runtime and one database, isolated by a TenantId on every tenant-aware record. Both are configuration of your own ASP.NET service, so the tenancy model is something you own and can change, not something a hosted platform decides for you.

You already have the first level

Every process instance carries a TenantId that you set when you create it. It is a system parameter of the base engine, available with every Workflow Engine license, and a building block for the tenant handling in your application. Workflow Engine NEO adds tenant context across the built-in workflow operations of the HTTP API and can enforce tenant permissions when API security is enabled.

Process level, with every Workflow Engine licensecsharp
var createParams = new CreateInstanceParams
{
    SchemeCode = "OrderApproval",
    TenantId = "acme-corp",
    IdentityId = "user-42"
};

await runtime.CreateInstanceAsync(createParams);

// Read it back anywhere you hold the instance:
string tenantId = processInstance.TenantId;

One deployment, many customers

A single application instance serves every tenant. You stop provisioning an environment, a database, a monitoring stack, and an upgrade window per customer, and start managing one system.

Initial activity

Isolation is a choice, per tenant

A tenant can have its own database and its own WorkflowRuntime, or share both with others under a TenantId filter. Both models live in the same host, so one strict customer does not set the terms for everyone else.

The tenant travels with the process

TenantId is a system parameter set when the instance is created and immutable afterwards. Subprocesses inherit it, and merging one back never overwrites the parent, so tenant identity holds across the whole process tree.

Permission is checked separately

The Workflow-Api-Tenant-ID header picks the context. The caller’s permission claim decides whether that context is allowed, and a request for a tenant the token does not cover gets 403.

Shared and tenant data, side by side

Since v22.0.0 every kind of engine data is either shared or owned by one tenant. One standard approval scheme can serve most customers while the ones paying for custom routing get schemes of their own.

Onboarding is a registry call

Since v22.0.0 tenants register and unregister while the host runs. A customer signs up, your provisioning code adds the tenant, and the tenants already running never pause.

Current activity

Pick the isolation model per tenant

Most tenancy decisions get made once, early, by whoever set up the first customer, and the product lives with that decision for years. Workflow Engine does not force it. The tradeoff is operational simplicity against isolation strength, and you resolve it per tenant, inside one deployment, with the same code and the same schemes running in all three shapes.

Logical tenancy

One runtime, one database, filtered by TenantId

Tenants share a WorkflowRuntime and a data provider. Every tenant-aware record carries the TenantId, and Data API queries filter by the selected tenant while RPC operations validate the process tenant.

Pick it when

Many small customers, or an enterprise running one engine for HR, finance, and operations, where operational simplicity beats physical separation.

Physical tenancy

Own database, own WorkflowRuntime

Each tenant is registered with its own connection string and persistence provider, and gets its own runtime. Isolation happens at the persistence layer, and the tenant becomes its own backup and restore unit.

Pick it when

Regulated customers, data-residency commitments, and enterprise contracts that put database-level separation in writing.

Hybrid tenancy

Both models in one API host

Pass several WorkflowTenantCreationOptions entries to AddWorkflowTenants(). Each entry is a physical tenant with its own runtime and provider, and its TenantIds array assigns one or more logical tenants to it.

Pick it when

Most SaaS products end up here. The enterprise tier gets a dedicated database and everyone else shares one.

Hybrid tenancy, one host, three tenantscsharp
// acme-corp gets its own database and its own runtime.
// globex and stark share one database, isolated by TenantId.
builder.Services.AddWorkflowApiCore(options =>
{
    options.DefaultTenantId = null; // every request must name its tenant
});

builder.Services.AddWorkflowTenants(
    new WorkflowTenantCreationOptions
    {
        TenantIds = ["acme-corp"],
        ConnectionString = "Server=db-acme;Database=Workflow;...",
        PersistenceProviderId = PersistenceProviderId.Mssql
    },
    new WorkflowTenantCreationOptions
    {
        TenantIds = ["globex", "stark"],
        ConnectionString = "Server=db-shared;Database=Workflow;...",
        PersistenceProviderId = PersistenceProviderId.Postgres
    });

How the four approaches compare

ApproachWhat it takesWhat you get
One deployment per tenantThe baselineProvision, monitor, and upgrade a full application and database for every customer.Highest infrastructure cost, the most operational work, and the slowest onboarding.
Logical tenancyBuilt inOne WorkflowRuntime and one provider serving several tenant IDs.Lowest cost per tenant, one system to manage, data isolated by TenantId.
Physical tenancyBuilt inOne application deployment, one database and one WorkflowRuntime per tenant.Low infrastructure cost with full data isolation and per-tenant backup and restore.
Hybrid tenancyBuilt inOne API host that maps some tenant IDs to dedicated runtimes and groups the rest on shared ones.A different isolation level per customer tier, without a second API host.

One deployment per tenant is the row you are trying to leave. It is the only one of the four that gives a tenant its own application version, and it is also the only one where onboarding a customer means provisioning a new environment. Every option and setting for the other three is in the HTTP API multitenancy reference.

The engine enforces tenant isolation

Tenant leaks are rarely design failures. They are the one query somebody forgot to filter. Workflow Engine takes that filtering out of your hands. The tenant is resolved once per request, checked against the caller’s permissions, and applied by the data layer for every tenant-aware record, including statuses, parameters, timers, transition history, inbox entries, and approval history.

01

The request names its tenant

Every call carries the Workflow-Api-Tenant-ID header, defined as WorkflowApiConstants.TenantIdHeader. A request without it is rejected with WorkflowTenantIdNotProvidedException unless a default tenant is configured.

02

The locator resolves the runtime

IWorkflowTenantLocator maps the logical tenant ID to a physical tenant through the request-scoped snapshot, and hands back that tenant’s WorkflowRuntime and IDataProvider.

03

The claim decides access

The header selects the context. It does not grant access to it. The caller’s WorkflowApiPermissions claim must allow the selected tenant as well as the operation, otherwise the request is rejected with 403 Forbidden.

04

The data layer holds the line

Data API queries filter by the selected tenant and RPC operations validate the process tenant. A process from another tenant is returned as not found rather than exposed to the caller.

Permissions travel in one claim

Authentication stays in standard ASP.NET. The engine adds one compact claim that carries hierarchical allow and deny rules for operations and for tenants, built with a typed API: AllowAllTenants(), DenyAllTenants(), AllowAllTenantsExcept(...), and DenyAllTenantsExcept(...). Both the operation and the tenant are checked on every secured request.

Deny by default, in one line of config

In multi-tenant mode the documented recommendation is to set DefaultTenantId to null, so no request can quietly fall back to a tenant it did not ask for. A missing header then fails loudly instead of resolving to somebody’s data. This claim gates the HTTP operation and the tenant; who may run a command once the request is inside a process is a separate layer, covered on the workflow authorization page. The full permission model, claim format and builder API are in the HTTP API security documentation.

Tenant identity inside your own action codecsharp
public async Task ExecuteActionAsync(string name, ProcessInstance process,
    WorkflowRuntime runtime, string parameter, CancellationToken token)
{
    // TenantId is set at creation and immutable afterwards.
    var tenantId = process.TenantId;

    // Branch on the tenant: their SLA, their approval limit, their mailbox.
    var settings = await _settings.ForTenantAsync(tenantId, token);
}

One honest note on transactions. Workflow Engine is thread-safe but not transactional, and there is no transaction spanning several commands. Tenant isolation is enforced on every read and write; it is not a substitute for your own transaction boundaries.

A new customer signs up and nothing restarts

Onboarding is where most tenancy designs quietly fail. Adding a customer means a config change, a release, and a maintenance window, so sales ends up waiting on engineering. Since v22.0.0 the tenant registry accepts changes while the host is running. Your signup flow calls the registry, the registry publishes a new immutable snapshot for new requests, and requests already in flight finish on the snapshot they started with. Every other tenant keeps processing workflows through the whole thing.

Runtime tenant registration (since v22.0.0)csharp
var registry = services.GetRequiredService<IWorkflowTenantRegistry>();

// Onboarding: the customer exists in your billing system, now give them a tenant.
var tenants = await registry.RegisterTenantsAsync(new WorkflowTenantCreationOptions
{
    TenantIds = ["acme-corp"],
    PersistenceProviderId = PersistenceProviderId.Mssql,
    ConnectionString = connectionString
});

// Offboarding is the same call in reverse.
await registry.UnregisterTenantsAsync(tenants.Single());

Onboarding

Register the tenant with a connection string and a provider. Give it a dedicated database now, or add it to a shared one and move it later.

Offboarding

Unregister the tenant. In the physical model its data sits in a database you can archive, hand over, or drop as a whole, which is what a deletion request usually asks for.

What it does not do

Registration does not provision infrastructure. Creating the database, applying its schema, and storing its secrets stay in your provisioning code.

Which license each tenancy model needs

There are two levels here and it matters which one you are buying. Process-level tenancy, the immutable TenantId you set when you create a process instance, belongs to the base engine. Full hybrid multitenancy, meaning the tenant-aware HTTP API with physical, logical, and mixed tenants plus tenant permissions, is a licensed Workflow Engine NEO capability. It is included in all four editions of Workflow Engine NEO: Subscription, Business, SaaS, and Enterprise.

Product and editionFull hybrid multitenancyWhat you get
Workflow Engine FreeNo editionsNot includedProcess-level TenantId, no tenant-aware HTTP API.
Workflow EngineTeam editionNot includedProcess-level TenantId, no tenant-aware HTTP API.
Workflow EngineComplete editionNot includedProcess-level TenantId and multi-server, no tenant-aware HTTP API.
Workflow Engine NEOSubscription editionIncludedAnnual, updates included, full hybrid multitenancy.
Workflow Engine NEOBusiness editionIncludedPerpetual, for internal applications, with APIs, clustering, and full hybrid multitenancy.
Workflow Engine NEOSaaS editionIncludedPerpetual, for public platforms, with full hybrid multitenancy.
Workflow Engine NEOEnterprise editionIncludedAdds OEM redistribution, a white-label designer, and source access.

Your Workflow Runtime integration, database schema, and schemes carry over as you move between the embedded products. A prototype built on Workflow Engine Free can therefore move to a multi-tenant deployment without a workflow rewrite: the same WorkflowRuntime, same database schema, same schemes, and the same embeddable workflow designer. The side-by-side view is on the product comparison page, and the tier-by-tier matrix is on the pricing page.

Common questions

Direct answers to what SaaS teams ask while designing tenancy: isolation models, enforcement, onboarding, backups, and licensing.

  1. What is multitenancy in a workflow engine?

    Multitenancy lets one Workflow Engine deployment serve multiple customers or business units. The Core runtime can store a TenantId with each process instance, while the host application applies that value in its own selection and access-control logic. Workflow Engine NEO adds tenant-aware HTTP API operations and supports logical, physical, and hybrid routing. Logical tenants share a runtime and persistence boundary; physical tenants use a dedicated database or database schema and a runtime in each API host; hybrid combines both. Built-in tenant-aware operations scope process data to the selected tenant, while custom endpoints and external stores must enforce the same boundary.

  2. What is the difference between logical, physical, and hybrid tenancy?

    Logical tenancy maps several tenant IDs to one IWorkflowTenant, WorkflowRuntime, provider, and storage boundary. Physical tenancy gives a tenant a dedicated database or database schema and a separate runtime in each API host. Hybrid tenancy registers both shapes in one host; each physical registration can contain one or more logical IDs. Database-per-tenant can support independent backup and restore through database tooling, while schema-level and shared-database recovery depend on that tooling.

  3. Which product do I need for multitenancy?

    Full hybrid multitenancy means the tenant-aware Workflow Engine HTTP API with logical, physical, and mixed tenant registrations. It is included in every edition of Workflow Engine NEO: Subscription, Business, SaaS, and Enterprise. Workflow Engine Free and the Team and Complete editions of Workflow Engine include the Core TenantId building block but not this tenant-aware HTTP API layer. TenantId is assigned when a process is created; its typed property is read-only, and the tenant-aware Data API does not provide an operation for moving a persisted process between tenants.

  4. How does the API know which tenant a request belongs to?

    A request selects its tenant through Workflow-Api-Tenant-ID or the configured DefaultTenantId. IWorkflowTenantLocator resolves that ID against the request-scoped registry snapshot and supplies the matching WorkflowRuntime and data provider. Set DefaultTenantId to null when tenant-aware requests must provide the header. Tenant selection is routing, not authorization: when AddWorkflowApiSecurity() is registered and enabled, WorkflowApiPermissions must also allow the operation and tenant.

  5. Can one tenant read another tenant’s processes if it knows the process ID?

    Not through correctly configured built-in tenant-aware operations. Data API queries use the selected tenant scope, and tenant-validated single-process RPC operations reject a mismatch; those single-process endpoints use the same not-found response for a missing or foreign process. Bulk RPC returns an item result that can preserve the mismatch exception. When HTTP API security is enabled, permissions also check the operation and tenant. Custom endpoints, direct Core or provider calls, and external stores must enforce the host application’s tenant policy.

  6. Can I add a tenant without restarting the application?

    Yes, since v22.0.0, without restarting that API host. RegisterTenantsAsync publishes a new immutable snapshot for new requests, while in-flight requests finish on the snapshot they started with. A multi-node deployment must apply the change to each node. Database or database-schema provisioning and secret management remain outside the tenant registry and are the responsibility of the host application. When the built-in factory creates a tenant from WorkflowTenantCreationOptions, it runs supported Workflow Engine migrations by default for SQL Server, PostgreSQL, MySQL, Oracle, and SQLite unless disabled; MongoDB uses the version-specific scripts.

  7. Can I turn an existing single-tenant installation into a multi-tenant one?

    Yes, but existing records need an explicit migration and cutover plan. In a current v22.x schema, the application supplies TenantId for new process instances through CreateInstanceParams. Existing processes with an empty TenantId do not become shared data for named tenant requests; built-in tenant-aware process operations use exact tenant scope. Schemes, forms, and Global Parameters follow their own shared or fallback rules. The tenant-aware Data API does not provide a process move operation, so inventory and migrate the required records and shared assets before changing routing. An upgrade from an earlier product version also requires the provider-specific v22.0.0 database migration; MongoDB uses its update script.

  8. Does every tenant need its own WorkflowRuntime?

    No. Several logical tenant IDs can share one IWorkflowTenant, WorkflowRuntime, provider, and storage boundary. Each physical tenant registration has its own runtime in each API host and uses a dedicated database or database schema. In a Multi-Server deployment, each node creates its own runtime for the same registered boundary, so runtime counts are per host rather than deployment-wide.

  9. Can tenants share one workflow scheme and still have their own?

    Yes. A tenant can use tenant-specific schemes and shared schemes. Scheme resolution uses the tenant-specific scheme when available and otherwise falls back to the shared scheme; it does not expose another tenant’s private scheme. This shared fallback is specific to schemes, while processes, forms, and Global Parameters follow their own tenant-scoping APIs.

  10. Can different tenants run on different databases?

    Yes. Each physical tenant registration supplies its persistence provider and storage settings, which can point to a dedicated database or database schema. One HTTP API host can mix providers after the corresponding provider factories are registered; for example, call AddWorkflowApiMssql() and AddWorkflowApiPostgres() before registering those tenant groups. Workflow Engine ships providers for SQL Server, PostgreSQL, MySQL, Oracle, MongoDB, and SQLite. Data-residency and backup guarantees still depend on the configured database and operational tooling.

  11. Does Workflow Designer respect tenant isolation?

    Yes. The tenant-aware Designer endpoint selects the tenant from request context. A tenant can open its tenant-specific schemes and shared fallback schemes, but not another tenant’s private schemes. The same embeddable Workflow Designer component is used for single-tenant and multitenant deployments.

  12. How does TenantId behave inside subprocesses?

    TenantId is assigned when a process instance is created. Its typed property is read-only, and the tenant-aware Data API does not support moving a persisted process between tenants. A subprocess inherits the parent’s TenantId, and MergeIntoParentProcessIsProhibited prevents the child from overwriting the parent’s value when it merges back. Built-in tenant-aware operations use that process scope for related process data.

  13. How does multitenancy affect backups and restores?

    The backup boundary depends on the physical storage model. A database-per-tenant deployment can use database tooling to back up or restore that tenant independently. A schema-per-tenant deployment depends on the database product and backup tooling, while logical tenants sharing one database normally share its restore boundary. Workflow Engine’s registry routes tenants to those boundaries; it does not perform backups or move data between them.

  14. Do I have to run a separate cluster or a vendor cloud for this?

    No. Workflow Engine is a set of NuGet packages that run inside your own ASP.NET application and store state in your own databases. Multitenancy is configuration of that application, not a separate service to operate, so there is no broker, no worker fleet, and no vendor-hosted control plane in the path of a tenant request.

Bring your tenancy model to the demo

Bring the shape of your customer base. How many tenants, which ones need their own database, what your contracts promise about isolation. We will walk through the configuration that matches it, and the session runs an hour. If you would rather start in code, install the free tier, set TenantId on your first process instance, and request a trial key when you reach the HTTP API.

In production since 2014 at Dell, KPMG, Bosch, and GE Honda Aero Engines.

Still comparing options? The multitenancy documentation carries the full configuration reference, and the Workflow Engine NEO product page covers the rest of what the license unlocks.