data/skills/n8n-subworkflows/README.md
Expert guidance for building reusable, composable n8n sub-workflows — extracting shared logic into Execute Workflow Trigger / Execute Workflow pairs that behave like functions: typed inputs, a body that does the work, a returned output shape every caller can rely on.
| Inline logic | Sub-workflow | |
|---|---|---|
| Reuse | Copy-pasted across workflows | Called from many workflows |
| Bug fix | Fix in every copy (miss one → drift) | Fix once |
| Caller sees | Five+ nodes of detail | One node ("Parse date") |
| Testable alone | No | Yes (n8n_test_workflow with pinned input) |
| Swap implementation | Ripples to every copy | Behind the contract, callers untouched |
| Agent tool | Not directly | Typed trigger → fill-able tool |
The trigger declares typed inputs, the last node returns the output, and the caller invokes it through an Execute Workflow node. Get the input contract and the name right and it's reusable; get them wrong and it quietly becomes the next duplicate.
n8n_list_workflows / n8n_get_workflow to find an existing sub-workflow before duplicating (the MCP can't filter by tags, so the name is the discovery surface)$fromAI) and structured callers pass values; passthrough only for binary or zero-inputmode: all vs each — run once over all items, or N times over one each; prefer each over an internal Loop Over ItemswaitForSubWorkflow — block on the result vs fire-and-forget; each + false is the only true parallelizationSubworkflow:, <Domain>:, Tool: prefixes so the next search finds itmode: all on a body that assumes one item → aggregates everyone instead of one-per-inputundefined, no error anywhereActivates when you:
workflowInputs)mode: all and mode: each, or set waitForSubWorkflowExample queries:
mode: each vs all?"Main skill content — loaded when the skill activates.
mode all vs each, waitForSubWorkflow, the only true parallelizationThe three n8n-specific patterns in depth.
mode: all vs each with a worked per-customer contrastDiscovery is naming, because the MCP can't filter by tags.
Subworkflow:, <Domain>:, Tool:)n8n_list_workflows / n8n_get_workflow{
"type": "n8n-nodes-base.executeWorkflowTrigger",
"parameters": {
"workflowInputs": {
"values": [
{ "name": "customer_id", "type": "string" },
{ "name": "include_orders", "type": "boolean" }
]
}
}
}
{{ $json.customer_id }}
{{ $('When Executed by Another Workflow').first().json.customer_id }}
Execute Workflow → mode: "each"
Execute Workflow → mode: "each", options.waitForSubWorkflow: false
Subworkflow: Parse RFC2822 date, Customer: get by id, Tool: list credentialsHelper 3 — it matches no searchn8n-workflow-patterns: shape the orchestrating workflow there; decide which sections become sub-workflows here.
n8n-mcp-tools-expert: parameter formats for n8n_list_workflows, n8n_get_workflow, n8n_update_partial_workflow, and n8n_manage_datatable.
n8n-node-configuration: workflowInputs and the Define-Below / passthrough toggle are displayOptions-driven config on the Execute Workflow Trigger.
n8n-expression-syntax: reading inputs ($json, $('When Executed by Another Workflow')) and the legitimate final-Set exception.
n8n-error-handling: expected failures return { ok: false, error }; unexpected ones throw and route through error outputs.
n8n-validation-expert: validate the sub-workflow and callers — but an unrecognized input field won't surface, so verify field changes by hand.
n8n-code-javascript / n8n-code-python: a Code-node body's contract is still the trigger's typed inputs and the returned shape.
n8n-code-tool: the Custom Code Tool is the inline agent-tool option; a sub-workflow tool is the reusable, multi-step one.
n8n-agents: wiring a typed sub-workflow as an agent tool (zero-input and binary cases).
n8n-binary-and-data: passthrough triggers for binary, and why binary can't flow through an agent tool directly.
using-n8n-mcp-skills: when to reach for which skill across a build.
| Situation | Why not |
|---|---|
| One HTTP call with no logic | A trigger → HTTP → return wrapper adds a boundary for nothing |
| Tightly coupled to one caller's data shape | Extracting just relocates the coupling — fix the shape first |
| Performance-critical hot path | Each call adds (small but real) latency — profile before adding boundaries |
Everything else that's >5 nodes and conceptually one thing, or a generic concern (auth, retry, parsing, formatting), is a strong extract.
After using this skill, you should be able to:
mode: each vs all from whether the body assumes a single itemwaitForSubWorkflow deliberately, and know each + false is the only true parallelizationReturn Set nodeVersion: 1.0.0
Compatibility: n8n with n8n-nodes-base.executeWorkflowTrigger and n8n-nodes-base.executeWorkflow; typed workflowInputs (Define Below) on recent versions.
Remember: a sub-workflow is a function. Its API is the trigger's typed inputs and the last node's output shape — make both explicit, name it so it's found, and call it with the mode its body expects.