diff --git a/Elsa.sln b/Elsa.sln
index 0a70dbda2..f88e4d6ef 100644
--- a/Elsa.sln
+++ b/Elsa.sln
@@ -230,6 +230,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AspNet.MongoDb
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Workflows.Api.Rpc", "src\modules\Elsa.Workflows.Api.Rpc\Elsa.Workflows.Api.Rpc.csproj", "{0A94D69A-61AC-4654-83AB-B24445628A99}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.AspNet.DynamicActivityProvider", "src\samples\aspnet\Elsa.Samples.AspNet.DynamicActivityProvider\Elsa.Samples.AspNet.DynamicActivityProvider.csproj", "{85E13383-7C39-4719-AAC0-0B357C3A97C7}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -576,6 +578,10 @@ Global
{0A94D69A-61AC-4654-83AB-B24445628A99}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A94D69A-61AC-4654-83AB-B24445628A99}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A94D69A-61AC-4654-83AB-B24445628A99}.Release|Any CPU.Build.0 = Release|Any CPU
+ {85E13383-7C39-4719-AAC0-0B357C3A97C7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {85E13383-7C39-4719-AAC0-0B357C3A97C7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {85E13383-7C39-4719-AAC0-0B357C3A97C7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {85E13383-7C39-4719-AAC0-0B357C3A97C7}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{155227F0-A33B-40AA-A4B4-06F813EB921B} = {61017E64-6D00-49CB-9E81-5002DC8F7D5F}
@@ -680,5 +686,6 @@ Global
{3A377A6A-F735-4010-9E00-72E8BEB8F1F2} = {9B4F139F-7D26-435C-A561-89E65A67A8E5}
{DAACE93A-866E-428E-B2A9-AFAC79F9C7A4} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
{0A94D69A-61AC-4654-83AB-B24445628A99} = {B08B4E00-C2AB-48F3-8389-449F42AEF179}
+ {85E13383-7C39-4719-AAC0-0B357C3A97C7} = {56C2FFB8-EA54-45B5-A095-4A78142EB4B5}
EndGlobalSection
EndGlobal
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/ActivityProviders/ApiActivityProvider.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/ActivityProviders/ApiActivityProvider.cs
new file mode 100644
index 000000000..fb17ff329
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/ActivityProviders/ApiActivityProvider.cs
@@ -0,0 +1,161 @@
+using System.Text;
+using System.Text.Json;
+using Elsa.Expressions.Models;
+using Elsa.Extensions;
+using Elsa.Http;
+using Elsa.Samples.AspNet.DynamicActivityProvider.Models;
+using Elsa.Workflows.Core;
+using Elsa.Workflows.Core.Contracts;
+using Elsa.Workflows.Core.Models;
+using Elsa.Workflows.Management.Extensions;
+using Humanizer;
+
+namespace Elsa.Samples.AspNet.DynamicActivityProvider.ActivityProviders;
+
+///
+/// Provides activities based on API descriptions (see /Data/apis.json).
+///
+public class ApiActivityProvider : IActivityProvider
+{
+ private readonly IActivityFactory _activityFactory;
+ private readonly IActivityDescriber _activityDescriber;
+
+ public ApiActivityProvider(IActivityFactory activityFactory, IActivityDescriber activityDescriber)
+ {
+ _activityFactory = activityFactory;
+ _activityDescriber = activityDescriber;
+ }
+
+ public async ValueTask> GetDescriptorsAsync(CancellationToken cancellationToken = default)
+ {
+ // Read JSON file.
+ var json = await File.ReadAllTextAsync("Data/apis.json", cancellationToken);
+
+ // Parse JSON into a list of API definitions.
+ var options = new JsonSerializerOptions
+ {
+ PropertyNamingPolicy = JsonNamingPolicy.CamelCase
+ };
+
+ var apisDefinitions = JsonSerializer.Deserialize(json, options)!;
+
+ // Create activity descriptors from API definitions.
+ var activities = ((await Task.WhenAll(apisDefinitions.Select(async x => await CreateActivityDescriptors(x, cancellationToken)))).SelectMany(x => x)).ToList();
+
+ // Return activity descriptors.
+ return activities;
+ }
+
+ ///
+ /// Creates activity descriptors from an API definition's endpoints.
+ ///
+ /// The API definition containing the endpoints.
+ /// An optional cancellation token.
+ /// A list of activity descriptors.
+ private async Task> CreateActivityDescriptors(ApiDefinition api, CancellationToken cancellationToken = default)
+ {
+ var descriptorTasks = api.Endpoints.Select(async endpoint => await CreateActivityDescriptorFromEndpointAsync(api, endpoint, cancellationToken)).ToList();
+
+ return await Task.WhenAll(descriptorTasks);
+ }
+
+ private async Task CreateActivityDescriptorFromEndpointAsync(ApiDefinition api, ApiEndpointDefinition endpoint, CancellationToken cancellationToken = default)
+ {
+ // Create a fully qualified type name for the activity ("{namespace}.{apiName}.{endpointName}").
+ var fullTypeName = $"Demo.{api.Name}.{endpoint.Name}";
+
+ // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract
+ // Justification: the JSON input may not have a parameters property.
+ var parameters = endpoint.Parameters ?? new List();
+
+ // Create inputs from endpoint parameters.
+ var inputs = parameters.Select(CreateInputDescriptor).ToList();
+
+ // Create inputs for the URL and method properties of the SendHttpRequest activity, which will be used as the implementation for executing instances of this activity.
+ // They are set in the constructor, and should not be visible to the user.
+ var urlInputDescriptor = await _activityDescriber.DescribeInputPropertyAsync>(x => x.Url, cancellationToken);
+ var urlInputReferenceId = Guid.NewGuid().ToString();
+ var methodInputDescriptor = await _activityDescriber.DescribeInputPropertyAsync>(x => x.Method, cancellationToken);
+ var methodInputReferenceId = Guid.NewGuid().ToString();
+
+ urlInputDescriptor.IsBrowsable = false;
+ urlInputDescriptor.ValueGetter = activity =>
+ {
+ var input = new Input(context =>
+ {
+ // Replace path parameters with input values.
+ var pathBuilder = new StringBuilder(endpoint.Path);
+
+ foreach (var parameter in parameters)
+ {
+ var inputDescriptor = inputs.FirstOrDefault(x => x.Name == parameter.Name);
+ var inputValue = inputDescriptor?.ValueGetter(activity) is Input inputValueInput ? context.Get(inputValueInput.MemoryBlockReference()) : default;
+ pathBuilder.Replace("{" + parameter.Name + "}", inputValue?.ToString());
+ }
+
+ return new(new Uri(api.BaseUrl, pathBuilder.ToString()));
+ }, urlInputReferenceId);
+ return input;
+ };
+
+ methodInputDescriptor.IsBrowsable = false;
+ methodInputDescriptor.ValueGetter = _ => new Input(endpoint.Method, methodInputReferenceId);
+
+ inputs.Add(urlInputDescriptor);
+ inputs.Add(methodInputDescriptor);
+
+ // Create an output descriptor based on the send HTTP request activity's ParsedContent property.
+ var outputDescriptor = await _activityDescriber.DescribeOutputProperty>(x => x.ParsedContent, cancellationToken);
+
+ return new ActivityDescriptor
+ {
+ Kind = ActivityKind.Task,
+ Category = "Demo",
+ Description = endpoint.Description,
+ Name = endpoint.Name,
+ TypeName = fullTypeName,
+ Namespace = $"Api.{api.Name}",
+ DisplayName = endpoint.Name.Humanize(),
+ Inputs = inputs,
+ Outputs = new[] { outputDescriptor },
+ Constructor = context =>
+ {
+ // The constructor is called when an activity instance of this type is requested.
+
+ // Create the activity instance.
+ var activity = _activityFactory.Create(context);
+
+ // Customize the activity type name.
+ activity.Type = fullTypeName;
+
+ // Configure the activity's URL and method properties.
+ activity.Url = new Input(new ReadReference(urlInputReferenceId));
+ activity.Method = new(new ReadReference(methodInputReferenceId));
+
+ return activity;
+ }
+ };
+ }
+
+ ///
+ /// Creates an input descriptor from an API parameter definition.
+ ///
+ /// The API parameter definition.
+ private static InputDescriptor CreateInputDescriptor(ApiParameterDefinition parameter)
+ {
+ var inputDescriptor = new InputDescriptor
+ {
+ Description = parameter.Description,
+ DefaultValue = parameter.DefaultValue,
+ Type = parameter.Type == "number" ? typeof(int) : typeof(string),
+ Name = parameter.Name,
+ DisplayName = parameter.Name.Humanize(),
+ IsSynthetic = true, // This is a synthetic property, i.e. it is not part of the activity's .NET type.
+ IsWrapped = true, // This property is wrapped within an Input object.
+ UIHint = InputUIHints.SingleLine,
+ ValueGetter = activity => activity.SyntheticProperties.GetValueOrDefault(parameter.Name),
+ ValueSetter = (activity, value) => activity.SyntheticProperties[parameter.Name] = value!,
+ };
+ return inputDescriptor;
+ }
+}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Data/apis.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Data/apis.json
new file mode 100644
index 000000000..a301fdc4d
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Data/apis.json
@@ -0,0 +1,43 @@
+[{
+ "name": "Reqres",
+ "baseUrl": "https://reqres.in",
+ "endpoints": [
+ {
+ "name": "GetUsers",
+ "path": "/api/users",
+ "method": "GET",
+ "description": "Get all users"
+ },
+ {
+ "name": "GetUser",
+ "path": "/api/users/{UserId}",
+ "method": "GET",
+ "description": "Get a user",
+ "params": [
+ {
+ "name": "UserId",
+ "type": "number",
+ "description": "User id"
+ }
+ ]
+ },
+ {
+ "name": "CreateUser",
+ "path": "/api/users",
+ "method": "POST",
+ "description": "Create a user",
+ "params": [
+ {
+ "name": "UserName",
+ "type": "string",
+ "description": "User name"
+ },
+ {
+ "name": "Job",
+ "type": "string",
+ "description": "User job"
+ }
+ ]
+ }
+ ]
+}]
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Elsa.Samples.AspNet.DynamicActivityProvider.csproj b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Elsa.Samples.AspNet.DynamicActivityProvider.csproj
new file mode 100644
index 000000000..c7beee0d8
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Elsa.Samples.AspNet.DynamicActivityProvider.csproj
@@ -0,0 +1,30 @@
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Always
+
+
+
+
+
+
+
+
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiDefinition.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiDefinition.cs
new file mode 100644
index 000000000..8b4ce56a8
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiDefinition.cs
@@ -0,0 +1,3 @@
+namespace Elsa.Samples.AspNet.DynamicActivityProvider.Models;
+
+public record ApiDefinition(string Name, Uri BaseUrl, ICollection Endpoints);
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiEndpointDefinition.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiEndpointDefinition.cs
new file mode 100644
index 000000000..cec308a08
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiEndpointDefinition.cs
@@ -0,0 +1,10 @@
+using System.Text.Json.Serialization;
+
+namespace Elsa.Samples.AspNet.DynamicActivityProvider.Models;
+
+public record ApiEndpointDefinition(
+ string Name,
+ string Description,
+ string Path,
+ string Method,
+ [property: JsonPropertyName("params")] ICollection Parameters);
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiParameterDefinition.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiParameterDefinition.cs
new file mode 100644
index 000000000..e1d24ac08
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Models/ApiParameterDefinition.cs
@@ -0,0 +1,3 @@
+namespace Elsa.Samples.AspNet.DynamicActivityProvider.Models;
+
+public record ApiParameterDefinition(string Name, string Type, string Description, string? DefaultValue);
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Program.cs b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Program.cs
new file mode 100644
index 000000000..a9d40ff2d
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Program.cs
@@ -0,0 +1,54 @@
+using Elsa.EntityFrameworkCore.Modules.Identity;
+using Elsa.EntityFrameworkCore.Modules.Management;
+using Elsa.EntityFrameworkCore.Modules.Runtime;
+using Elsa.Extensions;
+using Elsa.Samples.AspNet.DynamicActivityProvider.ActivityProviders;
+
+var builder = WebApplication.CreateBuilder(args);
+var services = builder.Services;
+
+// Add Elsa services.
+services.AddElsa(elsa => elsa
+ // Expose API endpoints.
+ .UseWorkflowsApi()
+
+ .UseWorkflowManagement(management => management.UseEntityFrameworkCore())
+ .UseWorkflowRuntime(runtime => runtime.UseEntityFrameworkCore())
+
+ // Configure identity so that we can create a default admin user.
+ .UseIdentity(identity =>
+ {
+ identity.UseAdminUserProvider();
+ identity.UseEntityFrameworkCore();
+ identity.TokenOptions = options =>
+ {
+ options.SigningKey = "secret-token-signing-key";
+ options.AccessTokenLifetime = TimeSpan.FromDays(1);
+ };
+ })
+
+ // HTTP workflows.
+ .UseHttp()
+
+ // Use default authentication (JWT).
+ .UseDefaultAuthentication(auth => auth.UseAdminApiKey())
+);
+
+// Add custom activity provider.
+services.AddActivityProvider();
+
+services.AddCors(cors => cors.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()));
+
+// Configure middleware pipeline.
+var app = builder.Build();
+
+if (app.Environment.IsDevelopment())
+ app.UseDeveloperExceptionPage();
+
+// Configure the HTTP request pipeline.
+app.UseAuthentication();
+app.UseAuthorization();
+app.UseCors();
+app.UseWorkflowsApi();
+app.UseWorkflows();
+app.Run();
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Properties/launchSettings.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Properties/launchSettings.json
new file mode 100644
index 000000000..dbd9fccfb
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Properties/launchSettings.json
@@ -0,0 +1,37 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iisExpress": {
+ "applicationUrl": "http://localhost:41572",
+ "sslPort": 44398
+ }
+ },
+ "profiles": {
+ "http": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "http://localhost:5015",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "https": {
+ "commandName": "Project",
+ "dotnetRunMessages": true,
+ "launchBrowser": true,
+ "applicationUrl": "https://localhost:7145;http://localhost:5015",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ }
+ }
+}
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Readme.md b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Readme.md
new file mode 100644
index 000000000..b4920accf
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Readme.md
@@ -0,0 +1,28 @@
+# Dynamic Activity Providers
+
+This sample project demonstrates how to implement a dynamic activity provider.
+
+## Overview
+
+The key to providing activities dynamically to the activity registry is to implement the `IActivityProvider` interface. This interface defines a single method, `GetDescriptorsAsync`, which returns a list of activity descriptors.
+
+In this example, we are loading API endpoint definitions from a JSON file located in the `Data` folder and create an activity descriptor for each endpoint.
+The activity descriptor is then used to create an activity type, which is then registered with the activity registry.
+
+This example could be applied to any other data source, such as a database or a web service, Open API specifications, GraphQL, etc.
+
+## Usage
+
+1. Run the sample project.
+2. Open the workflow editor.
+3. Notice the new activities in the toolbox.
+4. Drag one of the new activities onto the canvas.
+5. Notice the new activity in the canvas.
+6. Run the workflow.
+7. Notice the new activity being executed.
+
+## Notes
+
+This project provides sample workflows that you can import into the workflow editor. The workflows are located in the `Workflows` folder.
+You can then use the workflow editor to inspect the workflows and see how the activities are used.
+When imported, you can invoke the workflows using the sample-request.http file located in the root folder containing this project.
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/create-user-workflow.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/create-user-workflow.json
new file mode 100644
index 000000000..ad297d4ae
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/create-user-workflow.json
@@ -0,0 +1,236 @@
+{
+ "id": "0ad67e344f664e4ba40430933db6383d",
+ "definitionId": "2a29bd086ba047ad82c296f380b2b21d",
+ "name": "Create User",
+ "createdAt": "2023-07-13T14:06:07.932781+00:00",
+ "version": 9,
+ "variables": [],
+ "inputs": [],
+ "outputs": [],
+ "outcomes": [],
+ "customProperties": {},
+ "isReadonly": false,
+ "isLatest": true,
+ "isPublished": true,
+ "options": {
+ "autoUpdateConsumingWorkflows": false
+ },
+ "root": {
+ "type": "Elsa.Flowchart",
+ "version": 1,
+ "id": "Flowchart1",
+ "metadata": {},
+ "customProperties": {
+ "NotFoundConnectionsKey": [],
+ "canStartWorkflow": false,
+ "runAsynchronously": false
+ },
+ "start": "HttpEndpoint1",
+ "activities": [
+ {
+ "path": {
+ "typeName": "String",
+ "expression": {
+ "type": "Literal",
+ "value": "/users"
+ },
+ "memoryReference": {
+ "id": "540fb073-7da0-40e4-affd-7d12b83bd41b"
+ }
+ },
+ "supportedMethods": {
+ "typeName": "String[]",
+ "expression": {
+ "type": "Object",
+ "value": "[\u0022POST\u0022]"
+ },
+ "memoryReference": {
+ "id": "cd84bc03-5395-449b-96eb-27d5ad17ca53"
+ }
+ },
+ "authorize": {
+ "typeName": "Boolean",
+ "expression": {
+ "type": "Literal",
+ "value": "False"
+ },
+ "memoryReference": {
+ "id": "HttpEndpoint1:input-1"
+ }
+ },
+ "policy": null,
+ "parsedContent": {
+ "typeName": "Object",
+ "memoryReference": {
+ "id": "0a9e4d85af2149119068e324e3e61e32"
+ }
+ },
+ "routeData": null,
+ "queryStringData": null,
+ "headers": null,
+ "result": null,
+ "id": "HttpEndpoint1",
+ "name": null,
+ "type": "Elsa.HttpEndpoint",
+ "version": 1,
+ "customProperties": {
+ "canStartWorkflow": true,
+ "runAsynchronously": false
+ },
+ "metadata": {
+ "designer": {
+ "position": {
+ "x": 120,
+ "y": 260
+ }
+ }
+ }
+ },
+ {
+ "statusCode": {
+ "typeName": "System.Net.HttpStatusCode, System.Net.Primitives",
+ "expression": {
+ "type": "Literal",
+ "value": "OK"
+ },
+ "memoryReference": {
+ "id": "WriteHttpResponse1:input-1"
+ }
+ },
+ "content": {
+ "typeName": "Object",
+ "expression": {
+ "type": "JavaScript",
+ "value": "getParsedContentFromCreateUser1()"
+ },
+ "memoryReference": {
+ "id": "457df76f-3bdd-4e7e-ba8a-10ebed80281d"
+ }
+ },
+ "contentType": {
+ "typeName": "String",
+ "expression": {
+ "type": "Literal",
+ "value": "application/json"
+ },
+ "memoryReference": {
+ "id": "4673dc8b-e2ee-48c2-8b7a-5c3e2c3a7560"
+ }
+ },
+ "responseHeaders": {
+ "typeName": "Elsa.Http.Models.HttpResponseHeaders, Elsa.Http",
+ "expression": {
+ "type": "Literal",
+ "value": "{}"
+ },
+ "memoryReference": {
+ "id": "WriteHttpResponse1:input-2"
+ }
+ },
+ "id": "WriteHttpResponse1",
+ "name": null,
+ "type": "Elsa.WriteHttpResponse",
+ "version": 1,
+ "customProperties": {
+ "canStartWorkflow": false,
+ "runAsynchronously": false
+ },
+ "metadata": {
+ "designer": {
+ "position": {
+ "x": 640,
+ "y": 260
+ }
+ }
+ }
+ },
+ {
+ "expectedStatusCodes": [],
+ "unmatchedStatusCode": null,
+ "url": {
+ "typeName": "System.Uri, System.Private.Uri",
+ "memoryReference": {
+ "id": "44cb8368-b578-4b02-beb7-cbac55359ab6"
+ }
+ },
+ "method": {
+ "typeName": "String",
+ "memoryReference": {
+ "id": "d39ebcb9-8943-4d76-8299-7761aef1f8e3"
+ }
+ },
+ "content": null,
+ "contentType": null,
+ "authorization": null,
+ "requestHeaders": {
+ "typeName": "Elsa.Http.Models.HttpRequestHeaders, Elsa.Http",
+ "expression": {
+ "type": "Literal",
+ "value": "{}"
+ },
+ "memoryReference": {}
+ },
+ "parsedContent": null,
+ "result": null,
+ "id": "CreateUser1",
+ "name": null,
+ "type": "Demo.Reqres.CreateUser",
+ "version": 0,
+ "customProperties": {
+ "canStartWorkflow": false,
+ "runAsynchronously": false
+ },
+ "metadata": {
+ "designer": {
+ "position": {
+ "x": 428,
+ "y": 255
+ }
+ }
+ },
+ "userName": {
+ "typeName": "String",
+ "expression": {
+ "type": "JavaScript",
+ "value": "getParsedContentFromHttpEndpoint1().user;"
+ },
+ "memoryReference": {
+ "id": "14ebd00e-bd1a-4953-a933-d49439ed418c"
+ }
+ },
+ "job": {
+ "typeName": "String",
+ "expression": {
+ "type": "JavaScript",
+ "value": "getParsedContentFromHttpEndpoint1().job;"
+ },
+ "memoryReference": {
+ "id": "e92af470-3d83-4891-96b4-e83331d0896e"
+ }
+ }
+ }
+ ],
+ "connections": [
+ {
+ "source": {
+ "activity": "HttpEndpoint1",
+ "port": "Done"
+ },
+ "target": {
+ "activity": "CreateUser1",
+ "port": "In"
+ }
+ },
+ {
+ "source": {
+ "activity": "CreateUser1",
+ "port": "Done"
+ },
+ "target": {
+ "activity": "WriteHttpResponse1",
+ "port": "In"
+ }
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-all-users-workflow.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-all-users-workflow.json
new file mode 100644
index 000000000..e4baa0daa
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-all-users-workflow.json
@@ -0,0 +1 @@
+{"id":"906cd28a12454b06b71bb724cd5891d0","definitionId":"4b3bbe91862c42ba921e21f70b429d8e","name":"Get Users","createdAt":"2023-07-13T14:02:19.276895+00:00","version":20,"variables":[{"id":"c4c6fbc0926f40d7a93e61eb7668b40b","name":"Users","typeName":"Object","isArray":true,"storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"}],"inputs":[],"outputs":[],"outcomes":[],"customProperties":{},"isReadonly":false,"isLatest":true,"isPublished":false,"options":{"autoUpdateConsumingWorkflows":false},"root":{"type":"Elsa.Flowchart","version":1,"id":"Flowchart1","metadata":{},"customProperties":{"NotFoundConnectionsKey":[],"canStartWorkflow":false,"runAsynchronously":false},"start":"HttpEndpoint1","activities":[{"path":{"typeName":"String","expression":{"type":"Literal","value":"users/all"},"memoryReference":{"id":"6f30b933-ed1c-47fb-9d13-7b50f425e806"}},"supportedMethods":{"typeName":"String[]","expression":{"type":"Object","value":"[\u0022GET\u0022]"},"memoryReference":{"id":"d7ee590e-02b4-485d-8730-cc8656c55a99"}},"authorize":{"typeName":"Boolean","expression":{"type":"Literal","value":"False"},"memoryReference":{"id":"HttpEndpoint1:input-1"}},"policy":{"typeName":"String","expression":{"type":"Literal"},"memoryReference":{"id":"HttpEndpoint1:input-2"}},"parsedContent":null,"routeData":null,"queryStringData":null,"headers":null,"result":null,"id":"HttpEndpoint1","name":null,"type":"Elsa.HttpEndpoint","version":1,"customProperties":{"canStartWorkflow":true,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":120,"y":260}}}},{"statusCode":{"typeName":"System.Net.HttpStatusCode, System.Net.Primitives","expression":{"type":"Literal","value":"OK"},"memoryReference":{"id":"WriteHttpResponse1:input-1"}},"content":{"typeName":"Object","expression":{"type":"JavaScript","value":"const users = getUsers();\nreturn users;"},"memoryReference":{"id":"09552413-74b1-4156-86a3-6679feeec83e"}},"contentType":{"typeName":"String","expression":{"type":"Literal","value":"application/json"},"memoryReference":{"id":"a7b8ff37-34a3-4400-88b2-026f19de8901"}},"responseHeaders":{"typeName":"Elsa.Http.Models.HttpResponseHeaders, Elsa.Http","expression":{"type":"Literal","value":"{}"},"memoryReference":{"id":"WriteHttpResponse1:input-2"}},"id":"WriteHttpResponse1","name":null,"type":"Elsa.WriteHttpResponse","version":1,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":920,"y":260}}}},{"variable":{"id":"c4c6fbc0926f40d7a93e61eb7668b40b","name":"Users","typeName":"Object","value":"","storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"},"value":{"typeName":"Object","expression":{"type":"JavaScript","value":"getParsedContentFromGetUsers1()"},"memoryReference":{"id":"b1f4ed34-25d8-42f4-a59b-03541b11bfa6"}},"id":"SetVariable1","name":null,"type":"Elsa.SetVariable","version":1,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":660,"y":260}},"displayText":"Set Users"}},{"expectedStatusCodes":[],"unmatchedStatusCode":null,"url":{"typeName":"System.Uri, System.Private.Uri","memoryReference":{"id":"9ba5ad9b-b88a-45de-8e08-aec043e22a10"}},"method":{"typeName":"String","memoryReference":{"id":"ab690185-cda3-44e6-8aaa-bb1c2a2e7941"}},"content":null,"contentType":null,"authorization":null,"requestHeaders":{"typeName":"Elsa.Http.Models.HttpRequestHeaders, Elsa.Http","expression":{"type":"Literal","value":"{}"},"memoryReference":{}},"parsedContent":null,"result":null,"id":"GetUsers1","name":null,"type":"Demo.Reqres.GetUsers","version":0,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":460,"y":260}}}}],"connections":[{"source":{"activity":"HttpEndpoint1","port":"Done"},"target":{"activity":"GetUsers1","port":"In"}},{"source":{"activity":"SetVariable1","port":"Done"},"target":{"activity":"WriteHttpResponse1","port":"In"}},{"source":{"activity":"GetUsers1","port":"Done"},"target":{"activity":"SetVariable1","port":"In"}}]}}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-user-workflow.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-user-workflow.json
new file mode 100644
index 000000000..0e06e5525
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/Workflows/get-user-workflow.json
@@ -0,0 +1 @@
+{"id":"cfd60b01f85148b29e99b72d8c91754f","definitionId":"91390522c8674589bf9383e9c8e05447","name":"Get User","createdAt":"2023-07-13T14:10:52.554187+00:00","version":9,"variables":[{"id":"be5481f1f8f64df39012282404fbe7c7","name":"User","typeName":"Object","isArray":false,"value":"","storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"},{"id":"abd12e6b760840e38e7e79e8430d33db","name":"UserId","typeName":"Int32","isArray":false,"value":"0","storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"}],"inputs":[],"outputs":[],"outcomes":[],"customProperties":{},"isReadonly":false,"isLatest":true,"isPublished":true,"options":{"autoUpdateConsumingWorkflows":false},"root":{"type":"Elsa.Flowchart","version":1,"id":"Flowchart1","metadata":{},"customProperties":{"NotFoundConnectionsKey":[],"canStartWorkflow":false,"runAsynchronously":false},"start":"HttpEndpoint1","activities":[{"path":{"typeName":"String","expression":{"type":"Literal","value":"users/{userId}"},"memoryReference":{"id":"36cc8af8-3b83-49fd-aca2-1b8ba40583e8"}},"supportedMethods":{"typeName":"String[]","expression":{"type":"Object","value":"[\u0022GET\u0022]"},"memoryReference":{"id":"7389a64a-0fdb-4b8b-af55-72bad1eb72c9"}},"authorize":{"typeName":"Boolean","expression":{"type":"Literal","value":"False"},"memoryReference":{"id":"HttpEndpoint1:input-1"}},"policy":null,"parsedContent":null,"routeData":{"typeName":"ObjectDictionary","memoryReference":{"id":"ed6d5b4f6e794477837ee29e746e8ccb"}},"queryStringData":null,"headers":null,"result":null,"id":"HttpEndpoint1","name":null,"type":"Elsa.HttpEndpoint","version":1,"customProperties":{"canStartWorkflow":true,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":260,"y":220}}}},{"variable":{"id":"abd12e6b760840e38e7e79e8430d33db","name":"UserId","typeName":"Int32","value":"0","storageDriverTypeName":"Elsa.Workflows.Core.Services.WorkflowStorageDriver, Elsa.Workflows.Core"},"value":{"typeName":"Object","expression":{"type":"JavaScript","value":"getRouteDataFromHttpEndpoint1()[\u0022userid\u0022]"},"memoryReference":{"id":"4ae05d95-e23f-4158-b53f-1043728d2ad1"}},"id":"SetVariable1","name":null,"type":"Elsa.SetVariable","version":1,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":520,"y":220}},"displayText":"Set UserId"}},{"expectedStatusCodes":[],"unmatchedStatusCode":null,"url":{"typeName":"System.Uri, System.Private.Uri","memoryReference":{"id":"31406d9b-528a-4149-aed6-4dacfd6d566a"}},"method":{"typeName":"String","memoryReference":{"id":"91cbdddc-1df1-42ed-b63a-8ed7cfae115a"}},"content":null,"contentType":null,"authorization":null,"requestHeaders":{"typeName":"Elsa.Http.Models.HttpRequestHeaders, Elsa.Http","expression":{"type":"Literal","value":"{}"},"memoryReference":{}},"parsedContent":{"typeName":"Object","memoryReference":{"id":"be5481f1f8f64df39012282404fbe7c7"}},"result":null,"id":"GetUser1","name":null,"type":"Demo.Reqres.GetUser","version":0,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":760,"y":220}}},"userId":{"typeName":"Int32","expression":{"type":"JavaScript","value":"getUserId()"},"memoryReference":{"id":"66cf67ac-5c36-48cd-8a3a-d24fb831e1b0"}}},{"statusCode":{"typeName":"System.Net.HttpStatusCode, System.Net.Primitives","expression":{"type":"Literal","value":"OK"},"memoryReference":{"id":"WriteHttpResponse1:input-1"}},"content":{"typeName":"Object","expression":{"type":"JavaScript","value":"getUser()"},"memoryReference":{"id":"36f78ecf-9b4c-4245-b1af-a44d9730b52e"}},"contentType":{"typeName":"String","expression":{"type":"Literal","value":"application/json"},"memoryReference":{"id":"5146a788-d413-4ea4-9ac7-99fcf717bb33"}},"responseHeaders":{"typeName":"Elsa.Http.Models.HttpResponseHeaders, Elsa.Http","expression":{"type":"Literal","value":"{}"},"memoryReference":{"id":"WriteHttpResponse1:input-2"}},"id":"WriteHttpResponse1","name":null,"type":"Elsa.WriteHttpResponse","version":1,"customProperties":{"canStartWorkflow":false,"runAsynchronously":false},"metadata":{"designer":{"position":{"x":940,"y":220}}}}],"connections":[{"source":{"activity":"HttpEndpoint1","port":"Done"},"target":{"activity":"SetVariable1","port":"In"}},{"source":{"activity":"SetVariable1","port":"Done"},"target":{"activity":"GetUser1","port":"In"}},{"source":{"activity":"GetUser1","port":"Done"},"target":{"activity":"WriteHttpResponse1","port":"In"}}]}}
\ No newline at end of file
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.Development.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.Development.json
new file mode 100644
index 000000000..0c208ae91
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.Development.json
@@ -0,0 +1,8 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ }
+}
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.json b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.json
new file mode 100644
index 000000000..10f68b8c8
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/appsettings.json
@@ -0,0 +1,9 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.AspNetCore": "Warning"
+ }
+ },
+ "AllowedHosts": "*"
+}
diff --git a/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/sample-requests.http b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/sample-requests.http
new file mode 100644
index 000000000..84b2f1c7b
--- /dev/null
+++ b/src/samples/aspnet/Elsa.Samples.AspNet.DynamicActivityProvider/sample-requests.http
@@ -0,0 +1,17 @@
+### Create a new user
+POST https://localhost:5001/workflows/users
+Content-Type: application/json
+Accept: application/json
+
+{
+ "name": "Bob",
+ "job": "Postman"
+}
+
+### Get all users
+GET https://localhost:5001/workflows/users/all
+Accept: application/json
+
+### Get a user
+GET https://localhost:5001/workflows/users/2
+Accept: application/json