Reusing Logic with Subworkflows
Factor shared steps into a child workflow once, then call it from many parents with a Subworkflow node so you maintain the logic in a single place.
Overview
As your automation grows, the same handful of steps tend to show up in workflow after workflow: notify a Slack channel, write a record to your CRM, format and email a report. Copying those steps into every workflow that needs them means that when the logic changes, you have to find and edit each copy. The Subworkflow node solves this in Spojit by letting one workflow (the parent) invoke another workflow (the child) as a single step, passing data in and receiving the child's final output back.
The mental model is a function call. The child is a normal, standalone workflow with its own trigger and its own nodes. When a parent reaches a Subworkflow node, the parent pauses, the child runs end to end, and the child's final output flows back into the parent so downstream nodes can use it. Because every parent points at the same child, editing the child updates the behavior everywhere at once. This is the building block for decomposition (breaking a large workflow into readable pieces) and for shared team processes (one canonical "send notification" or "update CRM" routine that the whole workspace reuses).
Before You Start
- Build the child workflow first. It needs its own trigger; a Manual trigger works well for a child that is only ever called by a parent, because the data the parent passes in arrives as the run input.
- Decide what the child returns. The child's final node output is what the parent receives, so make the last step produce exactly the data the parent needs.
- Both the parent and the child must live in the same workspace, since the Subworkflow node picks the child from a list of workspace workflows.
Steps
Step 1: Build and save the child workflow
Open the Workflow Designer and create the workflow you want to reuse. Give it a clear, action-style name like Send Slack Notification or Upsert CRM Contact so it is easy to recognize in the picker later. Add a trigger (a Manual trigger is the common choice for a reusable child) and the nodes that do the work, then save. A child can be as simple as one Connector node in Direct mode calling send-message on your Slack connection, or as involved as a full pipeline of Transform, Condition, and Connector nodes.
Step 2: Reference the incoming data inside the child
The data a parent passes in arrives as the child's run input, so reference it with {{ input }} in the child's nodes (for example {{ input.channel }} or {{ input.message }}). Keep the input shape small and explicit: a child that expects {{ input.email }} and {{ input.name }} is far easier to call correctly than one that expects a sprawling object. Treat the input fields as the child's published contract.
Step 3: Add a Subworkflow node in the parent
Open the parent workflow in the Workflow Designer, add a Subworkflow node where the shared logic belongs, and connect it into the flow. In the node's properties panel set the Workflow field to the child you saved in Step 1 by picking it from the workspace list.
Step 4: Map the Input
Fill in the Input field with the data the child expects, using upstream variables from the parent. If your child reads {{ input.channel }} and {{ input.message }}, pass an object whose keys match, for example:
{
"channel": "#orders",
"message": "New order from {{ order.customerName }}"
}
Whatever you put here becomes the child's {{ input }}. Because the keys are the contract between parent and child, keep them stable so existing parents keep working when you edit the child.
Step 5: Use the child's output downstream
When the child finishes, its final node output returns to the parent and is available to the nodes after the Subworkflow node through that node's output variable. Wire a Condition node to branch on a returned field, or feed a returned value into a later Connector or Send Email node, exactly as you would with the output of any other node. The parent stays paused the whole time the child runs, then resumes once the result is back.
How runs appear in history
A parent run and each child run are tracked as separate entries in your execution history. When you open the parent run you see the Subworkflow step, and the child has its own run record with its own per-node detail. This separation is useful: if something fails inside the child, you can open the child's run directly to see which node failed and why, instead of digging through one giant combined log. For more on reading run records, see the related articles below.
When to reach for a subworkflow
Use a subworkflow when the same sequence of steps appears in more than one workflow, when a single workflow has grown long enough that splitting a section out makes the canvas readable again, or when a team wants one canonical routine (a standard notification, a standard CRM write) that everyone calls rather than reinvents. If logic only ever runs in one place and is short, a subworkflow adds indirection for no gain: keep it inline.
Tips
- Name children for what they do, not where they are called from.
Notify On-Call Slackreads better in the Workflow picker thanHelper 3. - Keep each child's
{{ input }}contract small and documented in the workflow description, so anyone adding a Subworkflow node knows exactly which keys to pass. - Edit the child once to change behavior everywhere. A fix to the shared
Update CRMchild takes effect immediately for every parent that calls it, with no need to touch the parents. - Have the child's last node return a tidy, predictable object (for example a single status flag and an id) so parents can branch on it cleanly with a Condition node.
Common Pitfalls
- Deep nesting. A child can itself call another child, but keep nesting shallow. Several layers of subworkflows make a failure hard to trace across that many separate run records and make the call graph difficult to reason about.
- Mismatched input keys. If the parent's Input uses
{{ input.msg }}but the child reads{{ input.message }}, the child sees nothing in that field. Keep the keys identical on both sides. - Forgetting that edits ripple. Because changing a child affects every parent at once, a well-meant tweak can break callers you forgot about. Check which workflows call a child before editing it, and test on a small scope first.
- Expecting the parent to keep running in parallel. The parent pauses while the child runs; it does not continue alongside the child. If you need concurrent work, use a Parallel node, not a subworkflow.
Related Articles
- Using Subworkflow Nodes covers the node's fields and basic behavior in the designer.
- How to Build a Reusable Subworkflow Library walks through assembling a set of shared children for your team.
- Monitoring Workflow Executions explains how to read the separate run records a parent and its children produce.
- Using Parallel Nodes is the right tool when you need branches to run concurrently instead of pausing for a child.
- Working with Variables and Templates shows how to build the Input object and reference a child's returned output.
Learn More
- Subworkflow node reference in the Spojit developer docs.
- Parallel node reference for running branches concurrently.
- Condition node reference for branching on a child's returned output.