Flow Studio
UserForm Node Config
Full configuration schema for the UserFormNode — formId, actorExpression, inputMap, ports, and timeout settings.
Config Schema
{
"nodeType": "UserForm",
"config": {
"formId": "form-employee-expense-request",
"actorExpression": "$output.fetchEmployee.managerId",
"title": "Expense Approval Request",
"description": "Please review and submit the expense claim for {{$output.fetchEmployee.name}}",
"inputMap": {
"employeeId": "$output.fetchEmployee.employeeId",
"claimAmount": "$output.parseClaim.amount",
"currency": "$output.parseClaim.currency",
"category": "$output.parseClaim.category"
},
"submitPort": "submitted",
"cancelPort": "cancelled",
"timeoutSeconds": 86400,
"timeoutPort": "timeout",
"taskPriority": "high"
}
}
Config Fields
| Field | Type | Required | Description |
|---|---|---|---|
formId | string | Yes | Atlas Forms form ID to render |
actorExpression | expression | Yes | User ID or array of user IDs who can complete this task |
title | string/expression | No | Task title shown in WorkDesk inbox |
description | string/expression | No | Task description with expression interpolation |
inputMap | object | No | Form field key → expression for pre-population |
submitPort | string | No | Port key on submit (default: "main") |
cancelPort | string | No | Port key on cancel (default: "cancelled") |
timeoutSeconds | number | No | Auto-cancel if not submitted within time limit |
Executor Pattern
[NodeCapability(CapabilityType.Form)]
public class UserFormExecutor : BaseNodeExecutor
{
public override async Task<NodeExecutionResult> ExecuteAsync(NodeExecutionContext ctx)
{
var config = ctx.GetConfig<UserFormConfig>();
var actorId = ctx.EvaluateExpression<string>(config.ActorExpression);
var initialValues = config.InputMap.ToDictionary(
kv => kv.Key,
kv => ctx.EvaluateExpression<object>(kv.Value));
// Suspend execution via HIL mechanism
await _hilService.SuspendAsync(new HILSuspensionRequest
{
ExecutionId = ctx.ExecutionId,
NodeId = ctx.NodeId,
ActorId = actorId,
FormId = config.FormId,
InitialValues = initialValues,
TaskTitle = ctx.EvaluateExpression<string>(config.Title),
TimeoutSeconds = config.TimeoutSeconds
});
// ExecuteAsync returns null here — the node is now suspended
// It will be called again when resume() is triggered
return NodeExecutionResult.Suspended();
}
}
Resume flow: When the user submits the form, the HIL resume API is called. The engine re-invokes
ExecuteAsync with the resumed context, which contains the form response data in ctx.ResumeData. The executor then returns Success(responseData, portKey: config.SubmitPort).