Flow Designer: Advanced Subflows and Custom Actions
Introduction
Flow Designer's real power emerges when you move beyond single-use flows into a reusable library of Subflows and Actions. This guide targets developers who already know the basics and want to build automation infrastructure that scales.
When to Build a Subflow
Build a Subflow when you find yourself copying the same sequence of steps into multiple flows. Common candidates:
- "Send approval and wait for outcome" pattern
- "Look up user and send Slack + email notification" pattern
- "Create CMDB relationship" pattern
- "Validate and route incident" pattern
Subflows are the equivalent of a function in traditional programming — named, documented, reusable.
Subflow Inputs and Outputs
Subflows communicate with their calling flows via defined inputs and outputs:
Defining Inputs
In the Subflow definition, add Inputs:
| Name | Type | Mandatory |
|---|---|---|
incident_record |
Reference (incident) | ✅ |
notify_manager |
True/False | ❌ |
escalation_level |
Integer | ❌ |
Accessing Inputs in Subflow Steps
Inputs appear as data pills under Subflow Inputs → <input_name> — reference them in any step just like trigger data.
Defining Outputs
In a Subflow step, use the Set Output Variable action:
Action: Set Output Variable
Name: approval_outcome
Value: [Ask For Approval → Approval State]
The calling flow receives approval_outcome and can branch on it.
Building a Reusable Approval Subflow
Subflow: Standard 2-Level Approval
Inputs:
record(Reference — any table)level1_approver_group(Reference — sys_user_group)level2_approver_group(Reference — sys_user_group)due_hours(Integer — default 48)
Steps:
[Ask For Approval — Level 1]
Table: [Inputs → record table]
Record: [Inputs → record]
Approvers: [Inputs → level1_approver_group]
Due: [Now + Inputs.due_hours hours]
↓
[Wait for Approval | Timeout: Inputs.due_hours hours]
↓
[If: Approved]
→ [Ask For Approval — Level 2] (same pattern)
[If: Rejected or Timed Out]
→ [Set Output: outcome = Rejected]
→ [End Subflow]
Outputs:
outcome(string: Approved / Rejected / Timed Out)final_approver(Reference — sys_user)
Building a Custom Action
When no out-of-the-box action fits, build a custom Action:
Flow Designer → New → Action
Action Inputs
Define what the action takes as parameters. Example — "Create CMDB Relationship" action:
| Input | Type |
|---|---|
parent_ci |
Reference (cmdb_ci) |
child_ci |
Reference (cmdb_ci) |
relationship_type |
String |
Action Script Step
// Inside the Action's Script step:
(function execute(inputs, outputs) {
var parentId = inputs.parent_ci.sys_id;
var childId = inputs.child_ci.sys_id;
var relType = inputs.relationship_type;
// Find the relationship type record
var relTypGR = new GlideRecord('cmdb_rel_type');
relTypGR.addQuery('name', relType);
relTypGR.setLimit(1);
relTypGR.query();
if (!relTypGR.next()) {
outputs.success = false;
outputs.error_message = 'Relationship type not found: ' + relType;
return;
}
// Create the relationship
var rel = new GlideRecord('cmdb_rel_ci');
rel.initialize();
rel.setValue('parent', parentId);
rel.setValue('child', childId);
rel.setValue('type', relTypGR.getUniqueValue());
var relId = rel.insert();
outputs.success = relId ? true : false;
outputs.relationship_sys_id = relId || '';
})(inputs, outputs);
Action Outputs
| Output | Type |
|---|---|
success |
True/False |
relationship_sys_id |
String |
error_message |
String |
Error Handling in Flows
Flow-Level Error Handler
Every flow can have an error handler path that fires when any step fails:
[Normal flow path]
↓
[If any step throws an error]
→ [Error Handler]
→ [Log error details]
→ [Send notification to on-call team]
→ [Update record state to 'Failed']
Configure in the flow canvas: click the flow header → Error Handler tab.
Try-Catch in Inline Scripts
For granular error handling within a specific step:
(function execute(inputs, outputs) {
try {
// Risky operation
var result = doSomethingRisky(inputs.value);
outputs.result = result;
outputs.success = true;
} catch(e) {
outputs.success = false;
outputs.error = e.message;
gs.error('Flow Action error: ' + e.message +
' | Stack: ' + e.stack);
}
})(inputs, outputs);
Debugging Flows
Execution Details
After a flow runs, check its execution log:
Flow Designer → Executions → select an execution
Each step shows:
- Input values at time of execution
- Output values produced
- Duration
- Error details if failed
Test Mode
Before activating a flow, use Test to run it against a specific record:
Flow canvas → Test → Select trigger record → Run Test
Test runs execute the full flow in your session and show step-by-step results without affecting production behavior.
Best Practices
- Build a Subflow library before building individual flows — identify common patterns first
- Define clear, typed inputs and outputs on every Subflow and Action
- Include error handling in every Action script
- Test every Action in isolation before using it in flows
- Document inputs, outputs, and expected behavior in the Action/Subflow description
- Version control Actions and Subflows in a dedicated scoped app
- Monitor flow execution logs weekly in production
Conclusion
Subflows and custom Actions transform Flow Designer from a tool for one-off automations into a platform for building an automation library. The investment in reusable components pays dividends every time a new flow can be built from existing building blocks rather than from scratch. Design your Action library the same way you'd design a software API — well-documented, clearly typed, and rigorously tested.