CAD Guide

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.

Keep reading this guide

Log in to access the full study guide and supercharge your preparation.