How to Build a Reusable Subworkflow Library
Create reusable workflow components that you can call from any workflow, reducing duplication and simplifying maintenance.
What This Integration Does
Over time, every Spojit account ends up with several workflows that do the same thing: look a customer up by email, validate an address, post a Slack alert in a standard format. Copy-pasting that logic into each new workflow is fast at first, but every time a downstream system changes (a new required field, a different Slack channel, an extra validation rule) you have to track down and patch every copy. Subworkflows let you extract that logic into a single workflow your other workflows call as one step, the same way you'd call a function in code.
Each subworkflow is a normal Spojit workflow with a Trigger and a Response node. The parent workflow drops in a Subworkflow node, picks the target workflow, maps its inputs, and (in sync mode) waits for the Response payload before continuing. Subworkflows show up in their own execution history, so failures are visible and debuggable on their own without polluting the parent's logs.
Prerequisites
- Write access to create and edit workflows in your workspace.
- At least one piece of logic that's used in two or more existing workflows - the candidate to extract.
- A naming convention for shared workflows (a
Shared:prefix or a dedicated folder works well).
Step 1: Identify the Reusable Logic
Pick a self-contained chunk of work with a clear input and output. Good candidates: customer lookup-or-create, address normalisation, formatting and sending a Slack alert, generating a PDF from a template. Avoid extracting tiny one-step pieces - the overhead of a Subworkflow node isn't worth it. Aim for at least 3 to 4 internal steps.
Step 2: Create the Subworkflow with a Manual Trigger
Create a new workflow named clearly (e.g. Shared: Customer Lookup or Create). Drop a Trigger node and set its type to Manual. The Manual trigger is what makes a workflow callable from a parent - its input schema defines the parameters the parent must pass. Declare each input field with a name and type (e.g. email as string, name as string).
Step 3: Build the Internal Logic
Reference the trigger inputs inline with handlebars - for example a Connector node calling the netsuite connector with the list-customers tool, filtered by email = "{{ trigger.email }}". Add a Condition node to branch on whether a customer was found, and a second Connector call to create-customer on the not-found branch. Keep the subworkflow focused - if it starts pulling in unrelated logic, split it into two.
Step 4: Return the Result with a Response Node
End the subworkflow with a Response node that returns a clean, stable payload to the caller. For the customer lookup example:
{
"customerId": "{{ customer.id }}",
"isNew": {{ wasCreated }},
"email": "{{ trigger.email }}"
}
Treat the Response shape as a public contract - other workflows depend on it, so changing field names is a breaking change.
Step 5: Call the Subworkflow from a Parent
In your main workflow, add a Subworkflow node. Pick the shared workflow from the dropdown, choose Sync execution mode so the parent waits for the response, and map the input fields from earlier step output (e.g. email = {{ step1.customer_email }}). Downstream nodes can then reference the result as {{ subworkflow1.customerId }}.
Step 6: Test Both Workflows Independently
Run the subworkflow on its own with the Manual trigger by supplying sample input - this catches contract bugs without dragging a parent through every test. Then run the parent and confirm the mapped variables land in the right place. Once both pass, repeat the extraction for the next candidate: a Send Slack Alert subworkflow, an Address Validation subworkflow, a Generate PDF Report subworkflow, and so on, building up a library.
Tips
- Use a
Shared:prefix or a dedicated folder so library workflows are easy to find amid feature workflows. - Pick Sync mode when the parent needs the result, and Async for fire-and-forget operations like notifications.
- Subworkflows can call other subworkflows up to 10 levels deep - use this for composition, but avoid deep chains because they get hard to debug.
- Version-sensitive logic (tax calculations, pricing rules) is the highest-value thing to extract - one fix updates every caller at once.
Common Pitfalls
- Changing the Response shape - renaming or removing fields silently breaks every parent. Add new fields instead and deprecate old ones gradually.
- Over-extraction - wrapping a single Connector call in a subworkflow adds overhead without saving meaningful duplication.
- Hidden side effects - if a subworkflow writes to a database or sends a message, document that on the workflow description so callers don't run it twice expecting it to be safe.
- Sync timeouts - very long-running subworkflows can hit the parent's wait timeout. Use Async mode and check the result later, or split the long work into its own scheduled workflow.
Testing
Run the subworkflow alone using the Manual trigger with a known-good input, then run it with deliberately bad input to confirm error paths. Once it's stable, call it from a sandbox parent workflow and verify the mapped variables and Response payload look right in the execution log before swapping it into production callers.