DEV Community

Pirate Prentice
Pirate Prentice

Posted on

n8n Execute Workflow Node: Call Sub-Workflows and Build Modular Automations [Free Workflow JSON]

The n8n Execute Workflow node lets one workflow call another workflow as a sub-routine. It's the foundation of modular n8n architecture: instead of one giant workflow that does everything, you build small, tested, reusable workflows and compose them together.

This guide covers every configuration option, the gotchas that trip people up, three production-ready patterns, and a free workflow JSON.


What the Execute Workflow node does

The Execute Workflow node synchronously calls another n8n workflow and waits for it to finish. The called workflow runs, produces output, and that output lands in the calling workflow as items — just like data from any other node.

This unlocks:

  • Modular design — shared logic lives in one place, called from many workflows
  • Reusable sub-routines — auth checks, data transforms, notification dispatchers
  • Testable units — test and debug each sub-workflow independently
  • Loop control — use Execute Workflow inside a Split In Batches loop to call logic per item

Trigger requirement in the called workflow

The called workflow must have an Execute Workflow Trigger node as its entry point (not a Webhook or Schedule Trigger). This trigger receives the data passed by the parent workflow.

Parent workflow → Execute Workflow node → Called workflow (starts at Execute Workflow Trigger)
Enter fullscreen mode Exit fullscreen mode

If the called workflow starts with any other trigger, n8n won't know where to inject the input data.


Configuration parameters

Parameter What it does
Source Where to find the workflow to call: Database (by workflow ID from the same n8n instance), Define below (inline workflow JSON, for testing). Use Database in production.
Workflow ID The numeric ID of the workflow to execute. Find it in the URL when you open the workflow in the n8n editor: /workflow/12345.
Wait for Sub-Workflow Completion Toggle. On (default) = the parent waits for the child to finish and receives its output. Off = fire-and-forget, no output returned.
Fields to pass Which fields from the current item to send to the child workflow. Leave empty to pass all fields.
Run Once with All Items Off (default) = the child workflow is called once per item (loops over your dataset). On = the child is called once with all items together (bulk mode).

Data flow

Parent → Child:
The items flowing into the Execute Workflow node are passed to the Execute Workflow Trigger in the child. Use {{ $json.fieldName }} expressions inside the child workflow to access them.

Child → Parent:
Whatever the child workflow's last node outputs becomes the output of the Execute Workflow node in the parent. Shape the child's output with a Set node at the end if you need a consistent schema.


Gotchas & common errors

1. The called workflow must be active

The Execute Workflow node calls the workflow by ID — but if the called workflow is deactivated (or has never been saved/activated), it will fail. Keep shared sub-workflows in "active" state even if they don't have a schedule or webhook.

2. Workflow ID changes on export/import

If you export a workflow and re-import it (to a new n8n instance or environment), it gets a new ID. Hard-coded workflow IDs in Execute Workflow nodes will break. Use environment variables or a lookup mechanism for the ID in multi-environment setups.

3. Infinite loops

If Workflow A calls Workflow B, and Workflow B calls Workflow A, you'll create an infinite loop that will exhaust memory. n8n doesn't detect this automatically. Sketch your workflow dependency graph before building.

4. Error propagation

Errors thrown inside the child workflow propagate to the parent and cause the Execute Workflow node to error. If you want the parent to continue even when the child fails, wrap the Execute Workflow node in an Error Trigger or set up a Try/Catch pattern with the workflow's Error Trigger workflow.

5. Run Once with All Items changes the output shape

In per-item mode (default), each child execution returns one item. In bulk mode (Run Once with All Items), the child receives all items at once and returns all its output items. This can change the downstream item count unexpectedly if you're not aware of which mode is active.

6. Credentials are not inherited

The child workflow runs with its own credentials. If the child calls an API, it needs its own credential configured — it won't use the parent's credentials.


3 workflow patterns

Pattern 1: Shared notification dispatcher

[Any workflow] → Execute Workflow ("Send Notification" sub-workflow)
  Input: { channel: "slack", message: "...", level: "error" }

"Send Notification" sub-workflow:
  Execute Workflow Trigger
  → Switch node (route on {{ $json.channel }}: slack | email | telegram)
  → Branch A: Slack node (send to #alerts)
  → Branch B: Gmail node (send to ops@company.com)
  → Branch C: Telegram node (send to ops chat)
Enter fullscreen mode Exit fullscreen mode

Use case: One notification workflow, called from 20 different workflows. Change the notification logic in one place.

Pattern 2: Reusable data enrichment

CRM webhook (new contact) → Execute Workflow ("Enrich Contact")
  Input: { email, company_name }

"Enrich Contact" sub-workflow:
  Execute Workflow Trigger
  → HTTP Request (fetch company data from Clearbit/Apollo)
  → HTTP Request (check LinkedIn via SerpApi)
  → Set node (normalise output schema)
  → [returns enriched contact object to parent]

Parent continues:
  → Airtable (update contact record with enriched data)
Enter fullscreen mode Exit fullscreen mode

Use case: Reuse the same enrichment logic from your CRM webhook, your bulk import workflow, and your nightly refresh — without duplicating nodes.

Pattern 3: Batch processor with per-item sub-workflow

Schedule Trigger (nightly)
→ Postgres (SELECT id, email FROM users WHERE needs_report = true)
→ Split In Batches (batch size: 10)
  → Execute Workflow ("Generate User Report")
    Input: { user_id, email }

"Generate User Report" sub-workflow:
  Execute Workflow Trigger
  → Postgres (SELECT user's activity data)
  → Basic LLM Chain (Anthropic: generate personalised summary)
  → Resend (send email to user)
  → Postgres (UPDATE users SET report_sent_at = NOW())
Enter fullscreen mode Exit fullscreen mode

Use case: Process 500 users nightly, each getting a personalised AI report, without building a 40-node monolith.


Free workflow JSON

Shared notification dispatcher pattern:

{
  "name": "Notification Dispatcher (Sub-Workflow)",
  "nodes": [
    {
      "parameters": {},
      "name": "Execute Workflow Trigger",
      "type": "n8n-nodes-base.executeWorkflowTrigger",
      "position": [240, 300]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            { "outputKey": "slack", "conditions": { "options": { "version": 2 }, "combinator": "and", "conditions": [{ "leftValue": "={{ $json.channel }}", "rightValue": "slack", "operator": { "type": "string", "operation": "equals" } }] } },
            { "outputKey": "email", "conditions": { "options": { "version": 2 }, "combinator": "and", "conditions": [{ "leftValue": "={{ $json.channel }}", "rightValue": "email", "operator": { "type": "string", "operation": "equals" } }] } }
          ]
        },
        "options": {}
      },
      "name": "Route by Channel",
      "type": "n8n-nodes-base.switch",
      "position": [460, 300]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            { "id": "1", "name": "sent", "value": true, "type": "boolean" },
            { "id": "2", "name": "channel", "value": "={{ $json.channel }}", "type": "string" }
          ]
        },
        "options": {}
      },
      "name": "Mark Sent",
      "type": "n8n-nodes-base.set",
      "position": [680, 300]
    }
  ],
  "connections": {
    "Execute Workflow Trigger": {
      "main": [[{ "node": "Route by Channel", "type": "main", "index": 0 }]]
    },
    "Route by Channel": {
      "main": [
        [{ "node": "Mark Sent", "type": "main", "index": 0 }],
        [{ "node": "Mark Sent", "type": "main", "index": 0 }]
      ]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Import via Settings → Import from URL/Clipboard. Add your Slack/Gmail nodes between the Switch output and Mark Sent. Note the workflow ID after saving, then reference it in your parent workflows' Execute Workflow nodes.


Next steps

Are you using Execute Workflow to build modular n8n architectures? Share your sub-workflow patterns in the comments — especially if you've solved the multi-environment ID problem.

Top comments (1)

Collapse
 
pirateprentice profile image
Pirate Prentice

Are you using the Execute Workflow node to build modular sub-workflow architectures — notification dispatchers, reusable enrichment steps, or batch processors? Drop your pattern below — always useful to see how people are structuring larger n8n projects.