$npx -y skills add czlonkowski/n8n-skills --skill n8n-subworkflowsBuild reusable, composable n8n sub-workflows. Use when extracting shared logic, building anything multi-step or reused across workflows, or any workflow over ~10 nodes — and whenever the user mentions sub-workflows, Execute Workflow, reuse, shared/common logic, modular workflows,
| 1 | # n8n Sub-workflows |
| 2 | |
| 3 | A sub-workflow is a reusable function. An **Execute Workflow Trigger** declares typed inputs, the body does the work, and the last node returns the output. A caller invokes it through an **Execute Workflow** node like any other step. |
| 4 | |
| 5 | That framing buys you the things functions buy you everywhere: encapsulation, reuse, testability, replaceability. It's the primary reuse mechanism in n8n, and it's badly underused. Without it, the same logic gets copy-pasted across workflows — then a bug gets fixed in two places, the third copy gets missed, and your "identical" copies quietly drift apart. |
| 6 | |
| 7 | This skill is about when to reach for a sub-workflow, how to define its input/output contract so callers (and agents) can actually use it, how to call it correctly (`all` vs `each`, blocking vs fire-and-forget), and how to name it so it gets found instead of rebuilt. |
| 8 | |
| 9 | --- |
| 10 | |
| 11 | ## The two non-negotiables |
| 12 | |
| 13 | Everything else is judgement. These two are not. |
| 14 | |
| 15 | ### 1. Search before you build |
| 16 | |
| 17 | Before you write logic for a generic problem, check whether a sub-workflow already does it. The community MCP can't filter workflows by tag, so the **name is the discovery surface**: |
| 18 | |
| 19 | ``` |
| 20 | n8n_list_workflows() # scan the library |
| 21 | n8n_get_workflow({ id: "<candidate>" }) # read its inputs/outputs + body |
| 22 | ``` |
| 23 | |
| 24 | If something fits, use it and tell the user ("I found `Subworkflow: Parse RFC2822 date` — using that"). If nothing fits, build it *with a discoverable name* so the next search finds it. The discovery convention (verb-first prefixes) lives in **NAMING_AND_DISCOVERY.md**. |
| 25 | |
| 26 | ### 2. The Execute Workflow Trigger uses "Define Below" with typed fields — not passthrough |
| 27 | |
| 28 | The trigger has two input modes. **Default to "Define Below"** with explicit typed fields. Define Below is the only mode that gives callers a schema to fill — it's what lets an AI agent pass values via `$fromAI` and what lets structured callers map fields cleanly. Passthrough has no schema, so the trigger can't be wired as a clean agent tool and structured callers have nothing to bind to. |
| 29 | |
| 30 | Two exceptions, and only two: |
| 31 | |
| 32 | - **Binary input.** Typed fields are JSON-only. If the sub-workflow must receive an image/file/PDF, you need passthrough so the `binary` slot flows through. |
| 33 | - **Zero inputs.** Define Below requires at least one field. A genuinely no-arg operation ("list active credentials", "current count") has nowhere to put an empty schema, so passthrough is the only option. |
| 34 | |
| 35 | Outside those two cases, passthrough is a bug. See "Inputs and outputs as a contract" below. |
| 36 | |
| 37 | --- |
| 38 | |
| 39 | ## Should this be a sub-workflow? |
| 40 | |
| 41 | You're about to write a chunk of logic. Run it through this: |
| 42 | |
| 43 | ``` |
| 44 | Could this plausibly be needed in another workflow? |
| 45 | └─ Yes → extract. |
| 46 | |
| 47 | Is it a generic concern (auth, retry, parsing, formatting, ID generation)? |
| 48 | └─ Almost always → extract. These are the canonical reusable sub-workflows. |
| 49 | |
| 50 | Is it >5 nodes and conceptually one thing? |
| 51 | └─ Probably extract, even if reuse isn't certain. It's better isolated. |
| 52 | |
| 53 | Is it one HTTP call with no logic around it? |
| 54 | └─ Don't. A sub-workflow that's just trigger → HTTP → return adds a boundary |
| 55 | for nothing. |
| 56 | |
| 57 | Is it tightly coupled to this one caller's data shape? |
| 58 | └─ Don't extract yet — fix the data shape first, or you just relocate the coupling. |
| 59 | ``` |
| 60 | |
| 61 | The reasons to extract go beyond reuse: |
| 62 | |
| 63 | - **Readability.** The caller shows one node ("Parse date") instead of five. |
| 64 | - **Testability.** Run the sub-workflow alone with pinned input (`n8n_test_workflow`). |
| 65 | - **Replaceability.** Swap the implementation without rippling to callers. |
| 66 | |
| 67 | A 20-node workflow is fine *if it's mostly a linear sequence of Execute Workflow calls and decisions* — each node has one purpose, and you inspect a section by opening the sub-workflow it calls. A 20-node workflow of inline transformations is not fine. If yours has 15+ nodes and isn't mostly sub-workflow calls and branches, extract more. |
| 68 | |
| 69 | --- |
| 70 | |
| 71 | ## Stateless vs. stateful (deliberately) |
| 72 | |
| 73 | Both are first-class. The choice is about intent and what the contract promises. |
| 74 | |
| 75 | **Stateless** — input in, output out, no I/O beyond that. The default for pure logic. When you need it again, you call it without worrying about side effects firing. |
| 76 | |
| 77 | - `Subworkflow: Parse RFC2822 date` — date string → ISO date or error. |
| 78 | - `Subworkflow: Compute MRR from subscription` — subscription object → number. |
| 79 | - `Subworkflow: Format invoice |