Skip to content

Custom Tasks

Akram El Assas edited this page Aug 2, 2025 · 64 revisions

Table of Contents

  1. Introduction
  2. General
    1. Creating a Custom Task
    2. Wexflow Task Class Example
    3. Task Status
    4. Settings
    5. Loading Files
    6. Loading Entities
    7. Need A Starting Point?
  3. Installing Your Custom Task in Wexflow
    1. .NET Framework 4.8 (Legacy Version)
    2. .NET 8.0+ (Stable Version)
    3. Referenced Assemblies
    4. Updating a Custom Task
    5. Using Your Custom Task
  4. Suspend/Resume
  5. Logging
  6. Files
  7. Entities
  8. Shared Memory
  9. Designer Integration
    1. Registering the Task
    2. Adding Settings
  10. How to Debug a Custom Task?

Introduction

Custom tasks are essential in any workflow engine, and Wexflow is no exception.

They allow you to extend the capabilities of Wexflow by integrating your own logic, operations, or external system interactions directly into workflows. Whether you're calling an API, processing files, querying a database, sending notifications, or triggering other services, custom tasks give you the flexibility to automate and orchestrate exactly what your application needs.

In Wexflow, tasks are modular building blocks. While Wexflow includes many built-in tasks, custom tasks empower you to build domain-specific solutions tailored to your environment or business logic.

By creating your own task class that inherits from Wexflow.Core.Task, you can plug into Wexflow's engine and leverage features such as:

  • Configuration through XML settings
  • File and entity sharing across tasks
  • Cross-task communication via shared memory
  • Logging and debugging integration
  • Suspend/resume/stop support
  • Flow control using conditions and switch values

This guide walks you through everything you need to know to build, register, and run your own custom tasks in Wexflow.

General

Creating a Custom Task

To create a custom task—for example, MyTask—follow these steps:

  1. Create a Class Library Project

    • For the legacy version (Wexflow on .NET Framework 4.8):
      Create a new Class Library (.NET Framework) project in Visual Studio and name it Wexflow.Tasks.MyTask.
      Make sure the target framework is set to .NET Framework 4.8.

    • For the stable version (Wexflow on .NET 8.0+):
      Use the .NET CLI to create the project:

      dotnet new classlib -n Wexflow.Tasks.MyTask

      Then edit the .csproj file to target .NET 8.0 or later:

      <TargetFramework>net8.0</TargetFramework>
  2. Add the Wexflow NuGet Package
    You can add the Wexflow package using the NuGet Package Manager in Visual Studio:

    Install-Package Wexflow

    Or via the CLI:

    dotnet add package Wexflow

Important: The project name must start with Wexflow.Tasks. and the output DLL file must also begin with Wexflow.Tasks. for Wexflow to recognize and load it.

Wexflow Task Class Example

To define your own task, inherit from the Task class and override either RunAsync (asynchronous) orRun (synchronous).

Example using RunAsync

If you want to use async/await functionality, override RunAsync instead of Run. Here's a simple example of a custom task:

using System;
using System.Xml.Linq;
using Wexflow.Core;
using Task = Wexflow.Core.Task;
using TaskStatus = Wexflow.Core.TaskStatus;

namespace Wexflow.Tasks.MyTask
{
    public class MyTask : Task
    {
        public MyTask(XElement xe, Workflow wf) : base(xe, wf)
        {
            // Initialize task settings from the XML element if needed.
            // Example: string settingValue = GetSetting("mySetting");
        }

        public async override System.Threading.Tasks.Task<TaskStatus> RunAsync()
        {
            try
            {
                // Check for workflow cancellation at the start of execution.
                // Always include this check in any long-running or looped logic.
                Workflow.CancellationTokenSource.Token.ThrowIfCancellationRequested();

                // Main task logic goes here.
                Info("Running my custom task...");

                // Simulate work using asynchronous delay.
                await System.Threading.Tasks.Task.Delay(2000);

                // Support workflow suspension. This call will block if the workflow is paused.
                // Only call WaitOne if cancellation hasn't already been requested.
                if (!Workflow.CancellationTokenSource.Token.IsCancellationRequested)
                {
                    WaitOne();
                }

                // Return success when the task completes successfully
                return new TaskStatus(Status.Success);
            }
            catch (OperationCanceledException)
            {
                // Don't suppress this exception; it allows proper workflow stop handling.
                throw;
            }
            catch (Exception ex)
            {
                // Log unexpected errors and return error status.
                ErrorFormat("An error occurred while executing the task.", ex);
                return new TaskStatus(Status.Error);
            }
        }
    }
}

Example using Run

If you don't need async/await functionality, you can use the synchronous Run method instead. Here's how the same task would look using Run:

using System;
using System.Threading;
using System.Xml.Linq;
using Wexflow.Core;
using Task = Wexflow.Core.Task;
using TaskStatus = Wexflow.Core.TaskStatus;

namespace Wexflow.Tasks.MyTask
{
    public class MyTask : Task
    {
        public MyTask(XElement xe, Workflow wf) : base(xe, wf)
        {
            // Initialize task settings from the XML element if needed
            // Example: string settingValue = GetSetting("mySetting");
        }

        public override TaskStatus Run()
        {
            try
            {
                // Check for workflow cancellation at the start of execution.
                // Always include this check in any long-running or looped logic.
                // Required for .NET 8.0+ stable version.
                Workflow.CancellationTokenSource.Token.ThrowIfCancellationRequested();

                // Main task logic goes here.
                Info("Running my custom task...");

                // WaitOne() enables suspend/resume support in .NET 8.0+.
                // Call this to pause the task when the workflow is suspended.
                // Only call WaitOne if cancellation hasn't already been requested.
                if (!Workflow.CancellationTokenSource.Token.IsCancellationRequested)
                {
                    WaitOne();
                }

                // Return success when the task completes successfully
                return new TaskStatus(Status.Success);
            }
            catch (ThreadInterruptedException)
            {
                // Required for .NET 4.8 legacy version.
                // Don't suppress this exception; it allows proper workflow stop handling.
                throw;
            }
            catch (OperationCanceledException)
            {
                // Required for .NET 8.0+ stable version.
                // Don't suppress this exception; it allows proper workflow stop handling.
                throw;
            }
            catch (Exception ex)
            {
                // Log unexpected errors and return error status.
                ErrorFormat("An error occurred while executing the task.", ex);
                return new TaskStatus(Status.Error);
            }
        }
    }
}

Task Status

Each task returns a TaskStatus object when it finishes performing its job. TaskStatus is composed of the following elements:

public Status Status { get; set; }
public bool Condition { get; set; }
public string SwitchValue { get; set; }

The Status can be one of the followings:

public enum Status
{
  Success,
  Warning,
  Error
}

For example, if a task performs an opetation on a collection of files and if this operation succeeds for all the files then its Status should be Success. Otherwise, if this operation succeeds for some files and fails for others then its Status should be Warning. Otherwise, if this operation fails for all the files then its Status should be Error.

The Condition property is designed for flowchart tasks (If and While). In addition to the Status of the task, a flowchart task returns either true or false after performing its operation.

The Condition property should always be set to false for sequential tasks.

The SwitchValue is designed to be used by Switch flowchart nodes. If you set a value in the SwitchValue property and use this task in a Switch flowchart node, the case corresponding to the value will be executed. Otherwise, if the Default case is set, it will be executed.

You can use the TaskStatus constructor that suits your needs.

Settings

To retrieve settings, you can use the following methods:

string settingValue = this.GetSetting("settingName");
string settingValue = this.GetSetting("settingName", defaultValue);
string[] settingValues = this.GetSettings("settingName");
bool settingValue = this.GetSettingBool("settingName", defaultValue);
int settingValue = this.GetSettingInt("settingName", defaultValue);
int[] settingValues = this.GetSettingsInt("settingName", defaultValue);

Loading Files

To load a file within a task, you can do it as follows:

this.Files.Add(new FileInf(path, this.Id));

Loading Entities

To load an entity within a task, you can do it as follows:

this.Entities.Add(myEntity);

Need a starting point?

Installing Your Custom Task in Wexflow

.NET Framework 4.8 (Legacy Version)

Once you've finished coding your custom task, compile the class library project and copy the Wexflow.Tasks.MyTask.dll assembly into one of the following folders:

  • C:\Program Files\Wexflow\
  • C:\Wexflow\Tasks\ (default for Wexflow .NET 4.8 version)

The Tasks folder path can be configured via the tasksFolder setting in the configuration file: C:\Wexflow\Wexflow.xml

Important: The namespace and DLL filename of your task must start with Wexflow.Tasks.

.NET 8.0+ (Stable Version)

If you're using the .NET 8.0+ version of Wexflow, copy Wexflow.Tasks.MyTask.dll to the appropriate platform-specific folder:

  • Windows:
    • .\Wexflow.Server
    • C:\Wexflow-netcore\Tasks
  • Linux:
    • /opt/wexflow/Wexflow.Server
    • /opt/wexflow/Wexflow/Tasks
  • macOS:
    • /Applications/wexflow/Wexflow.Server
    • /Applications/wexflow/Wexflow/Tasks

Referenced Assemblies

If your custom task depends on additional assemblies (DLLs), copy them as follows:

  • .NET 4.8: C:\Program Files\Wexflow\
  • .NET 8.0+:
    • Windows: .\Wexflow.Server or C:\Wexflow-netcore\Tasks
    • Linux: /opt/wexflow/Wexflow.Server or /opt/wexflow/Wexflow/Tasks
    • macOS: /Applications/wexflow/Wexflow.Server or /Applications/wexflow/Wexflow/Tasks

Updating a Custom Task

To update an existing custom task:

  1. Stop Wexflow Server
  2. Replace the old DLL and its referenced assemblies with the new versions in the correct folder.
  3. Start Wexflow Server:
  • .NET 4.8: Start the Wexflow Windows Service
  • .NET 8.0+:
    • Windows: Run .\run.bat or start Wexflow Service if you installed it as a Windows Service
    • Linux: Run sudo systemctl start wexflow
    • macOS: Run dotnet /Applications/wexflow/Wexflow.Server/Wexflow.Server.dll

Using Your Custom Task

Once installed, your task can be used in workflows like this:

<Task id="$int" name="MyTask" description="My task description" enabled="true">
    <Setting name="settingName" value="settingValue" />
</Task>

Important: Make sure the name attribute matches the class name of your task (e.g., MyTask).

You can also define settings for your task using the <Setting> elements, which can be accessed in your task code via:

string settingValue = this.GetSetting("settingName");
string settingValue = this.GetSetting("settingName", defaultValue);
string[] settingValues = this.GetSettings("settingName");
bool settingValue = this.GetSettingBool("settingName", defaultValue);
int settingValue = this.GetSettingInt("settingName", defaultValue);
int[] settingValues = this.GetSettingsInt("settingName", defaultValue);

You can then test your custom task by creating a new workflow using either the Designer or the XML editor.

Example using the XML editor:

<Workflow xmlns="urn:wexflow-schema" id="99" name="Workflow_MyWorkflow" description="Workflow_MyWorkflow">
    <Settings>
        <Setting name="launchType" value="trigger" />
        <Setting name="enabled" value="true" />
    </Settings>
    <Tasks>
        <Task id="1" name="MyTask" description="My task description" enabled="true">
            <Setting name="settingName" value="settingValue" />
        </Task>
    </Tasks>
</Workflow>

This workflow will appear in the Wexflow Manager. You can launch and monitor it from there.

That's it! You're now ready to create, install, and run your own custom tasks in Wexflow.

Suspend/Resume

For .NET 8.0+, if you want to enable suspend/resume for your custom task you need to use this.WaitOne(); in your custom task. Here is an example:

using System;
using System.Xml.Linq;
using Wexflow.Core;

namespace Wexflow.Tasks.MyTask
{
    public class MyTask : Task
    {
        public MyTask(XElement xe, Workflow wf) : base(xe, wf)
        {
            // Initialize task settings from the XML element if needed.
            // Example: string settingValue = GetSetting("mySetting");
        }

        public async override System.Threading.Tasks.Task<TaskStatus> RunAsync()
        {
            try
            {
                // Check for workflow cancellation at the start of execution.
                // Always include this check in any long-running or looped logic.
                Workflow.CancellationTokenSource.Token.ThrowIfCancellationRequested();

                // Main task logic goes here.
                Info("Running my custom task...");

                // Simulate work using asynchronous delay.
                await System.Threading.Tasks.Task.Delay(2000);

                // Support workflow suspension. This call will block if the workflow is paused.
                // Only call WaitOne if cancellation hasn't already been requested.
                if (!Workflow.CancellationTokenSource.Token.IsCancellationRequested)
                {
                    WaitOne();
                }

                // Return success when the task completes successfully
                return new TaskStatus(Status.Success);
            }
            catch (OperationCanceledException)
            {
                // Don't suppress this exception; it allows proper workflow stop handling.
                throw;
            }
            catch (Exception ex)
            {
                // Log unexpected errors and return error status.
                ErrorFormat("An error occurred while executing the task.", ex);
                return new TaskStatus(Status.Error);
            }
        }
    }
}

Logging

The following methods are available from the Task class for logging:

public void Info(string msg);
public void InfoFormat(string msg, params object[] args);
public void Debug(string msg);
public void DebugFormat(string msg, params object[] args);
public void Error(string msg);
public void ErrorFormat(string msg, params object[] args);
public void Error(string msg, Exception e);
public void ErrorFormat(string msg, Exception e, params object[] args);

Files

Files can be loaded in a task by calling the methods Add or AddRange:

this.Files.Add(myFile);
this.Files.AddRange(myFiles);

Then the files loaded can be selected in other tasks by their task Id as follows:

<Setting name="selectFiles" value="$taskId" />

To select the files loaded by the running instance of a workflow through the selectFiles settings option, you can do it as follows:

FileInf[] files = this.SelectFiles();

Entities

Entity is an abstract class having the Id of the task as property:

namespace Wexflow.Core
{
    public abstract class Entity
    {
        public int TaskId { get; set; }
    }
}

The Entity class is designed to be inherited by other classes such as objects retrieved from a database or a web service or an API or whatever. Then, these objects can be loaded in a task by calling the methods Add or AddRange:

this.Entities.Add(myEntity);
this.Entities.AddRange(myEntities);

Then, the entities loaded can be selected in other tasks by their task Id as follows:

<Setting name="selectEntities" value="$taskId" />

Entities are designed to be used in custom tasks.

To select entities loaded by the running instance of a workflow through the selectEntities settings option, you can do it as follows:

Entity[] entities = this.SelectEntities();

The Entity class could be very useful when working with custom tasks that manipulate objects from a database or Web Services for example.

Shared Memory

Tasks contains a Hashtable that can be used as a shared memory between them.

To add an object to the SharedMemory, simply proceed as follows:

this.SharedMemory.Add("myKey", myObject);

To retrieve an object from the SharedMemory, simply proceed as follows:

var myObject = this.SharedMemory["myKey"];

To remove an object from the SharedMemory, simply proceed as follows:

this.SharedMemory.Remove("myKey");

Important: Always access this.SharedMemory in RunAsync or Run method of a task to get updated values at runtime.

Designer Integration

Registering the Task

To make your custom task MyTask appear in the available tasks in the designer, simply open the file C:\Wexflow\TasksNames.json and add MyTask in it as follows:

[
...
{ "Name": "MyTask", "Description": "MyTask description."},
]

If you use the .NET 8.0+ version of Wexflow, you need to edit this file:

  • Windows: C:\Wexflow-netcore\TasksNames.json
  • Linux: /opt/wexflow/Wexflow/TasksNames.json
  • macOS: /Applications/wexflow/Wexflow/TasksNames.json

Adding Settings

You must also add the settings by opening the file C:\Wexflow\TasksSettings.json and adding your custom settings as follows:

{
...
"MyTask": [ {"Name": "settingName", "Required": true, "Type": "string", "List": [], "DefaultValue": ""} ],
}

If you use the .NET 8.0+ version of Wexflow, you need to edit this file:

  • Windows: C:\Wexflow-netcore\TasksSettings.json
  • Linux: /opt/wexflow/Wexflow/TasksSettings.json
  • macOS: /Applications/wexflow/Wexflow/TasksSettings.json

The available types are:

  • string
  • int
  • bool
  • password
  • list
  • user
  • record

user type refers to registered users in Wexflow.

record type refers to registered records in Wexflow.

If you choose list type, you have to set the available list options. Here is an example:

{
...
"MyTask": [ {"Name": "protocol", "Required": true, "Type": "list", "List": ["ftp", "ftps", "sftp"], "DefaultValue": ""} ],
}

That's it. MyTask will show up in the designer and when selected its settings will show up as well.

How to Debug a Custom Task?

1. Build Your Task DLL in Debug Mode

  • Compile your custom task project with Debug configuration.
  • Make sure the .pdb symbol files are generated alongside the .dll.
  • Copy both .dll and .pdb files to the Wexflow Tasks folder (e.g. C:\Wexflow-netcore\Tasks).

2. Attach Visual Studio Debugger to Wexflow Process

  • Launch Wexflow server.
  • In Visual Studio, go to Debug → Attach to Process...
  • Find and select the Wexflow process:
    • Enable Show processes from all users
    • If you're running Wexflow as a Windows Service (legacy), make sure the service is started and select: Wexflow.Server.exe
    • If you're running Wexflow as a Windows Service (stable via NSSM), make sure the service is started and select: dotnet.exe process that's running Wexflow.Server.dll.
    • If you're running Wexflow from the command prompt using dotnet Wexflow.Server.dll, Find and select the dotnet.exe process that's running Wexflow.Server.dll.
  • Click Attach.

3. Ensure Symbols (.pdb) Are Loaded

  • After attaching, open Modules window (Debug → Windows → Modules).
  • Locate your custom DLL (e.g. Wexflow.Tasks.MyTask.dll).
  • Check if symbols are loaded:
    • If not loaded, right-click the module → Load Symbols and browse to your .pdb file.
    • Confirm the symbol path is correct and matches your DLL.

4. Set Breakpoints in Your Task Code

  • Open your custom task source code in Visual Studio as Admin.
  • Set breakpoints inside the RunAsync or Run methods or anywhere you want to debug.

5. Trigger Your Workflow

  • Run the workflow that uses your custom task (via Designer or API).
  • When execution reaches your breakpoint, Visual Studio will break.

6. Additional Tips

  • Make sure your DLL in the Tasks folder is the same version you built and debugged.
  • If debugging symbols don’t load automatically, try deleting older DLLs and PDBs before copying new ones.
  • You can enable detailed logging inside your task with Info(), Debug(), and Error() calls for extra insights.
  1. Install Guide
  2. HTTPS/SSL
  3. Screenshots
  4. Docker
  5. Configuration Guide
    1. Wexflow Server
    2. Wexflow.xml
    3. Admin Panel
    4. Authentication
  6. Persistence Providers
  7. Getting Started
  8. Android App
  9. Local Variables
  10. Global Variables
  11. REST Variables
  12. Functions
  13. Cron Scheduling
  14. Command Line Interface (CLI)
  15. REST API Reference
    1. Introduction
    2. JWT Authentication
    3. Sample Clients
      1. C# Client
      2. JavaScript Client
      3. PHP Client
      4. Python Client
      5. Go Client
      6. Rust Client
      7. Ruby Client
      8. Java Client
      9. C++ Client
    4. Security Considerations
    5. Swagger
    6. Workflow Notifications via SSE
      1. C# SSE Client
      2. JavaScript SSE Client
      3. PHP SSE Client
      4. Python SSE Client
      5. Go SSE Client
      6. Rust SSE Client
      7. Ruby SSE Client
      8. Java SSE Client
      9. C++ SSE Client
    7. Endpoints
  16. Samples
    1. Sequential workflows
    2. Execution graph
    3. Flowchart workflows
      1. If
      2. While
      3. Switch
    4. Approval workflows
      1. Simple approval workflow
      2. OnRejected workflow event
      3. YouTube approval workflow
      4. Form submission approval workflow
    5. Workflow events
  17. Logging
  18. Custom Tasks
    1. Introduction
    2. General
      1. Creating a Custom Task
      2. Wexflow Task Class Example
      3. Task Status
      4. Settings
      5. Loading Files
      6. Loading Entities
      7. Need A Starting Point?
    3. Installing Your Custom Task in Wexflow
      1. .NET Framework 4.8 (Legacy Version)
      2. .NET 8.0+ (Stable Version)
      3. Referenced Assemblies
      4. Updating a Custom Task
      5. Using Your Custom Task
    4. Suspend/Resume
    5. Logging
    6. Files
    7. Entities
    8. Shared Memory
    9. Designer Integration
      1. Registering the Task
      2. Adding Settings
    10. How to Debug a Custom Task?
  19. Built-in Tasks
    1. File system tasks
    2. Encryption tasks
    3. Compression tasks
    4. Iso tasks
    5. Speech tasks
    6. Hashing tasks
    7. Process tasks
    8. Network tasks
    9. XML tasks
    10. SQL tasks
    11. WMI tasks
    12. Image tasks
    13. Audio and video tasks
    14. Email tasks
    15. Workflow tasks
    16. Social media tasks
    17. Waitable tasks
    18. Reporting tasks
    19. Web tasks
    20. Script tasks
    21. JSON and YAML tasks
    22. Entities tasks
    23. Flowchart tasks
    24. Approval tasks
    25. Notification tasks
    26. SMS tasks
  20. Run from Source
  21. Fork, Customize, and Sync
Clone this wiki locally