Developer Use Cases
Practical code recipes for common scenarios when implementing node manifests — from a minimal placeholder to a full suspension-based approval node.
1. New Node — Minimal Setup
Simple action node with no HIL, no sensitive data. Return Empty.
// MyNode.Config.cs
protected override NodeExecutorManifest? GetNodeExecutorManifest()
=> NodeExecutorManifest.Empty(ProcessElementTypeCode);
2. Field Receiving Upstream Input
Field populated automatically from upstream node's output data bag.
new NodeFieldDescriptor
{
FieldId = "recipientId",
Description = "User ID resolved from upstream lookup.",
ExpressionPolicy = new ExpressionPolicy
{
EvaluationStage = EvaluationStage.AtInputReady,
EvaluatorKind = EvaluatorKind.Template
},
DataFlowPolicy = new DataFlowPolicy
{
AcceptsUpstreamInput = true,
ExcludeFromInputMapping = false,
ExcludeFromOutputMapping = true
},
HilPolicy = new HilPolicy { SendToHil = false, DisplayMode = HilDisplayMode.Concealed },
SecurityPolicy = new SecurityPolicy()
}
3. Output Field for Downstream Nodes
Result produced by executor, passed to downstream nodes, persisted to memory.
new NodeFieldDescriptor
{
FieldId = "parsedResult",
Description = "Parsed output data for downstream processing.",
ExpressionPolicy = new ExpressionPolicy
{
EvaluationStage = EvaluationStage.AtInputReady,
EvaluatorKind = EvaluatorKind.None // set by executor
},
DataFlowPolicy = new DataFlowPolicy
{
AcceptsUpstreamInput = false,
EmitsToDownstream = true,
PersistsToMemory = true,
ExcludeFromInputMapping = true,
ExcludeFromOutputMapping = false
},
HilPolicy = new HilPolicy { SendToHil = false, DisplayMode = HilDisplayMode.Concealed },
SecurityPolicy = new SecurityPolicy()
}
4. Credential Field — Maximum Security
Full lockdown — masked, concealed, excluded from all mapping, elevated access required.
new NodeFieldDescriptor
{
FieldId = "credentialId",
Description = "Vault credential ID resolved via ICredentialResolver.",
ExpressionPolicy = new ExpressionPolicy
{
EvaluationStage = EvaluationStage.AtConfigLoad,
EvaluatorKind = EvaluatorKind.Template
},
DataFlowPolicy = new DataFlowPolicy
{
AcceptsUpstreamInput = false,
EmitsToDownstream = false,
PersistsToMemory = false,
ExcludeFromInputMapping = true,
ExcludeFromOutputMapping = true
},
HilPolicy = new HilPolicy
{
SendToHil = false,
DisplayMode = HilDisplayMode.Concealed,
InputMode = HilInputMode.Locked,
Label = "Credential",
Description = "Internal vault reference — not shown to reviewers."
},
SecurityPolicy = new SecurityPolicy
{
MaskInLogs = true,
MaskInOutput = true,
RequiresElevatedAccess = true
}
}
5. HIL Context-Only Field
Human sees the value for context but cannot change it.
new NodeFieldDescriptor
{
FieldId = "strategy",
Description = "Approval strategy — shown in inbox for context.",
ExpressionPolicy = new ExpressionPolicy
{
EvaluationStage = EvaluationStage.AtConfigLoad,
EvaluatorKind = EvaluatorKind.Template
},
DataFlowPolicy = new DataFlowPolicy
{
ExcludeFromInputMapping = true,
ExcludeFromOutputMapping = true
},
HilPolicy = new HilPolicy
{
SendToHil = true,
DisplayMode = HilDisplayMode.ReadableContext,
InputMode = HilInputMode.Locked,
Label = "Approval Strategy",
Description = "How votes are counted to reach a decision."
},
SecurityPolicy = new SecurityPolicy()
}
6. HIL Required Confirmation
Human must explicitly confirm or provide a value before the workflow continues.
new NodeFieldDescriptor
{
FieldId = "body",
Description = "Message body — human must confirm before sending.",
ExpressionPolicy = new ExpressionPolicy
{
EvaluationStage = EvaluationStage.AtInputReady,
EvaluatorKind = EvaluatorKind.Template
},
DataFlowPolicy = new DataFlowPolicy
{
AcceptsUpstreamInput = true,
ExcludeFromInputMapping = false,
ExcludeFromOutputMapping = true
},
HilPolicy = new HilPolicy
{
SendToHil = true,
DisplayMode = HilDisplayMode.ReadableContext,
InputMode = HilInputMode.RequiredFromHuman,
Label = "Message Body",
Description = "Read the full message. You must confirm it before it is sent."
},
SecurityPolicy = new SecurityPolicy()
}
7. Suspending Node with Approval Logic
Full manifest with 48h hard deadline approval suspension.
protected override NodeExecutorManifest? GetNodeExecutorManifest()
=> NodeExecutorManifest.From(
ProcessElementTypeCode,
fields: new[]
{
// ... field descriptors ...
},
suspensionPolicy: new SuspensionPolicy
{
TimeoutSeconds = 172800, // 48 hours
TimeoutPortKey = "expired",
TimeoutBehavior = TimeoutBehavior.AbsoluteDeadline,
ReminderIntervalSeconds = new[] { 86400, 43200, 3600 },
SlaThresholdSeconds = 86400, // 24h SLA
EmitSlaBreachEvent = true,
AllowAdminForceComplete = true,
CapturePreSuspensionSnapshot = true
}
);
8. JavaScript Expression Field
Field whose value is computed by a JavaScript snippet from multiple upstream inputs.
new NodeFieldDescriptor
{
FieldId = "payload",
Description = "Webhook payload built dynamically from upstream inputs.",
ExpressionPolicy = new ExpressionPolicy
{
EvaluationStage = EvaluationStage.AtInputReady,
EvaluatorKind = EvaluatorKind.JavaScript
},
DataFlowPolicy = new DataFlowPolicy
{
AcceptsUpstreamInput = true,
ExcludeFromInputMapping = false,
ExcludeFromOutputMapping = true
},
HilPolicy = new HilPolicy
{
SendToHil = true,
DisplayMode = HilDisplayMode.ReadableContext,
InputMode = HilInputMode.EditableOptional,
Label = "Webhook Payload",
Description = "The JSON payload to send. You may edit before sending."
},
SecurityPolicy = new SecurityPolicy()
}
// Config value stored in DB / workflow definition:
// JSON.stringify({ userId: inputs.customerId, amount: inputs.total })
9. Extension JSON Override (No Redeployment)
Override a field's HIL policy for a specific tenant via database without code changes.
{
"nodeTypeName": "email-smtp",
"suspensionPolicy": null,
"fields": [
{
"id": "body",
"hilPolicy": {
"sendToHil": true,
"displayMode": "ReadableContext",
"inputMode": "RequiredFromHuman",
"label": "Email Body",
"description": "Compliance requirement: you must confirm every email body."
}
}
]
}
// Only hilPolicy is overridden. expressionPolicy, dataFlowPolicy, securityPolicy
// keep their code-defined values.
10. Checklist — Adding Manifest to Existing Node
- ☐ Create
[NodeName].Config.cspartial in the executor project - ☐ Override
GetNodeExecutorManifest()— returnEmptyorFrom - ☐ For each meaningful field: create a
NodeFieldDescriptor - ☐ Set
FieldIdto match the camelCase property name on the settings class - ☐ Set
ExpressionPolicystage and kind appropriately - ☐ Set
DataFlowPolicyfor input/output participation - ☐ Set
HilPolicy.SendToHil: truefor fields a human needs to see - ☐ Set security masking for credentials and secrets
- ☐ Add
SuspensionPolicyif the node suspends - ☐ Verify
LogActivity()is called on every exit path in the executor