---
name: n8n-mcp-tools-expert
description: Expert guide for using n8n-mcp MCP tools effectively. Use when searching for nodes, validating configurations, accessing templates, managing workflows, or using any n8n-mcp tool. Provides tool selection guidance, parameter formats, and common patterns.
risk: critical
source: community
---

# n8n MCP Tools Expert

Master guide for using n8n-mcp MCP server tools to build workflows.

## When to Use
- You are using the `n8n-mcp` toolset to discover nodes, validate configs, or manage workflows.
- The task involves choosing the right MCP tool or understanding its expected parameters and usage pattern.
- You need guidance on workflow creation or editing through n8n MCP rather than through the n8n UI alone.

---

## Tool Categories

n8n-mcp provides tools organized into categories:

1. **Node Discovery** → SEARCH_GUIDE.md
2. **Configuration Validation** → VALIDATION_GUIDE.md
3. **Workflow Management** → WORKFLOW_GUIDE.md
4. **Template Library** - Search and deploy 2,700+ real workflows
5. **Documentation & Guides** - Tool docs, AI agent guide, Code node guides

---

## Quick Reference

### Most Used Tools (by success rate)

| Tool | Use When | Speed |
|------|----------|-------|
| `search_nodes` | Finding nodes by keyword | <20ms |
| `get_node` | Understanding node operations (detail="standard") | <10ms |
| `validate_node` | Checking configurations (mode="full") | <100ms |
| `n8n_create_workflow` | Creating workflows | 100-500ms |
| `n8n_update_partial_workflow` | Editing workflows (MOST USED!) | 50-200ms |
| `validate_workflow` | Checking complete workflow | 100-500ms |
| `n8n_deploy_template` | Deploy template to n8n instance | 200-500ms |

---

## Tool Selection Guide

### Finding the Right Node

**Workflow**:
```
1. search_nodes({query: "keyword"})
2. get_node({nodeType: "nodes-base.name"})
3. [Optional] get_node({nodeType: "nodes-base.name", mode: "docs"})
```

**Example**:
```javascript
// Step 1: Search
search_nodes({query: "slack"})
// Returns: nodes-base.slack

// Step 2: Get details
get_node({nodeType: "nodes-base.slack"})
// Returns: operations, properties, examples (standard detail)

// Step 3: Get readable documentation
get_node({nodeType: "nodes-base.slack", mode: "docs"})
// Returns: markdown documentation
```

**Common pattern**: search → get_node (18s average)

### Validating Configuration

**Workflow**:
```
1. validate_node({nodeType, config: {}, mode: "minimal"}) - Check required fields
2. validate_node({nodeType, config, profile: "runtime"}) - Full validation
3. [Repeat] Fix errors, validate again
```

**Common pattern**: validate → fix → validate (23s thinking, 58s fixing per cycle)

### Managing Workflows

**Workflow**:
```
1. n8n_create_workflow({name, nodes, connections})
2. n8n_validate_workflow({id})
3. n8n_update_partial_workflow({id, operations: [...]})
4. n8n_validate_workflow({id}) again
5. n8n_update_partial_workflow({id, operations: [{type: "activateWorkflow"}]})
```

**Common pattern**: iterative updates (56s average between edits)

---

## Critical: nodeType Formats

**Two different formats** for different tools!

### Format 1: Search/Validate Tools
```javascript
// Use SHORT prefix
"nodes-base.slack"
"nodes-base.httpRequest"
"nodes-base.webhook"
"nodes-langchain.agent"
```

**Tools that use this**:
- search_nodes (returns this format)
- get_node
- validate_node
- validate_workflow

### Format 2: Workflow Tools
```javascript
// Use FULL prefix
"n8n-nodes-base.slack"
"n8n-nodes-base.httpRequest"
"n8n-nodes-base.webhook"
"@n8n/n8n-nodes-langchain.agent"
```

**Tools that use this**:
- n8n_create_workflow
- n8n_update_partial_workflow

### Conversion

```javascript
// search_nodes returns BOTH formats
{
  "nodeType": "nodes-base.slack",          // For search/validate tools
  "workflowNodeType": "n8n-nodes-base.slack"  // For workflow tools
}
```

---

## Common Mistakes

### Mistake 1: Wrong nodeType Format

**Problem**: "Node not found" error

```javascript
// WRONG
get_node({nodeType: "slack"})  // Missing prefix
get_node({nodeType: "n8n-nodes-base.slack"})  // Wrong prefix

// CORRECT
get_node({nodeType: "nodes-base.slack"})
```

### Mistake 2: Using detail="full" by Default

**Problem**: Huge payload, slower response, token waste

```javascript
// WRONG - Returns 3-8K tokens, use sparingly
get_node({nodeType: "nodes-base.slack", detail: "full"})

// CORRECT - Returns 1-2K tokens, covers 95% of use cases
get_node({nodeType: "nodes-base.slack"})  // detail="standard" is default
get_node({nodeType: "nodes-base.slack", detail: "standard"})
```

**When to use detail="full"**:
- Debugging complex configuration issues
- Need complete property schema with all nested options
- Exploring advanced features

**Better alternatives**:
1. `get_node({detail: "standard"})` - for operations list (default)
2. `get_node({mode: "docs"})` - for readable documentation
3. `get_node({mode: "search_properties", propertyQuery: "auth"})` - for specific property

### Mistake 3: Not Using Validation Profiles

**Problem**: Too many false positives OR missing real errors

**Profiles**:
- `minimal` - Only required fields (fast, permissive)
- `runtime` - Values + types (recommended for pre-deployment)
- `ai-friendly` - Reduce false positives (for AI configuration)
- `strict` - Maximum validation (for production)

```javascript
// WRONG - Uses default profile
validate_node({nodeType, config})

// CORRECT - Explicit profile
validate_node({nodeType, config, profile: "runtime"})
```

### Mistake 4: Ignoring Auto-Sanitization

**What happens**: ALL nodes sanitized on ANY workflow update

**Auto-fixes**:
- Binary operators (equals, contains) → removes singleValue
- Unary operators (isEmpty, isNotEmpty) → adds singleValue: true
- IF/Switch nodes → adds missing metadata

**Cannot fix**:
- Broken connections
- Branch count mismatches
- Paradoxical corrupt states

```javascript
// After ANY update, auto-sanitization runs on ALL nodes
n8n_update_partial_workflow({id, operations: [...]})
// → Automatically fixes operator structures
```

### Mistake 5: Not Using Smart Parameters

**Problem**: Complex sourceIndex calculations for multi-output nodes

**Old way** (manual):
```javascript
// IF node connection
{
  type: "addConnection",
  source: "IF",
  target: "Handler",
  sourceIndex: 0  // Which output? Hard to remember!
}
```

**New way** (smart parameters):
```javascript
// IF node - semantic branch names
{
  type: "addConnection",
  source: "IF",
  target: "True Handler",
  branch: "true"  // Clear and readable!
}

{
  type: "addConnection",
  source: "IF",
  target: "False Handler",
  branch: "false"
}

// Switch node - semantic case numbers
{
  type: "addConnection",
  source: "Switch",
  target: "Handler A",
  case: 0
}
```

### Mistake 6: Not Using intent Parameter

**Problem**: Less helpful tool responses

```javascript
// WRONG - No context for response
n8n_update_partial_workflow({
  id: "abc",
  operations: [{type: "addNode", node: {...}}]
})

// CORRECT - Better AI responses
n8n_update_partial_workflow({
  id: "abc",
  intent: "Add error handling for API failures",
  operations: [{type: "addNode", node: {...}}]
})
```

---

## Tool Usage Patterns

### Pattern 1: Node Discovery (Most Common)

**Common workflow**: 18s average between steps

```javascript
// Step 1: Search (fast!)
const results = await search_nodes({
  query: "slack",
  mode: "OR",  // Default: any word matches
  limit: 20
});
// → Returns: nodes-base.slack, nodes-base.slackTrigger

// Step 2: Get details (~18s later, user reviewing results)
const details = await get_node({
  nodeType: "nodes-base.slack",
  includeExamples: true  // Get real template configs
});
// → Returns: operations, properties, metadata
```

### Pattern 2: Validation Loop

**Typical cycle**: 23s thinking, 58s fixing

```javascript
// Step 1: Validate
const result = await validate_node({
  nodeType: "nodes-base.slack",
  config: {
    resource: "channel",
    operation: "create"
  },
  profile: "runtime"
});

// Step 2: Check errors (~23s thinking)
if (!result.valid) {
  console.log(result.errors);  // "Missing required field: name"
}

// Step 3: Fix config (~58s fixing)
config.name = "general";

// Step 4: Validate again
await validate_node({...});  // Repeat until clean
```

### Pattern 3: Workflow Editing

**Most used update tool**: 99.0% success rate, 56s average between edits

```javascript
// Iterative workflow building (NOT one-shot!)
// Edit 1
await n8n_update_partial_workflow({
  id: "workflow-id",
  intent: "Add webhook trigger",
  operations: [{type: "addNode", node: {...}}]
});

// ~56s later...

// Edit 2
await n8n_update_partial_workflow({
  id: "workflow-id",
  intent: "Connect webhook to processor",
  operations: [{type: "addConnection", source: "...", target: "..."}]
});

// ~56s later...

// Edit 3 (validation)
await n8n_validate_workflow({id: "workflow-id"});

// Ready? Activate!
await n8n_update_partial_workflow({
  id: "workflow-id",
  intent: "Activate workflow for production",
  operations: [{type: "activateWorkflow"}]
});
```

---

## Detailed Guides

### Node Discovery Tools
See SEARCH_GUIDE.md for:
- search_nodes
- get_node with detail levels (minimal, standard, full)
- get_node modes (info, docs, search_properties, versions)

### Validation Tools
See VALIDATION_GUIDE.md for:
- Validation profiles explained
- validate_node with modes (minimal, full)
- validate_workflow complete structure
- Auto-sanitization system
- Handling validation errors

### Workflow Management
See WORKFLOW_GUIDE.md for:
- n8n_create_workflow
- n8n_update_partial_workflow (17 operation types!)
- Smart parameters (branch, case)
- AI connection types (8 types)
- Workflow activation (activateWorkflow/deactivateWorkflow)
- n8n_deploy_template
- n8n_workflow_versions

---

## Template Usage

### Search Templates

```javascript
// Search by keyword (default mode)
search_templates({
  query: "webhook slack",
  limit: 20
});

// Search by node types
search_templates({
  searchMode: "by_nodes",
  nodeTypes: ["n8n-nodes-base.httpRequest", "n8n-nodes-base.slack"]
});

// Search by task type
search_templates({
  searchMode: "by_task",
  task: "webhook_processing"
});

// Search by metadata (complexity, setup time)
search_templates({
  searchMode: "by_metadata",
  complexity: "simple",
  maxSetupMinutes: 15
});
```

### Get Template Details

```javascript
get_template({
  templateId: 2947,
  mode: "structure"  // nodes+connections only
});

get_template({
  templateId: 2947,
  mode: "full"  // complete workflow JSON
});
```

### Deploy Template Directly

```javascript
// Deploy template to your n8n instance
n8n_deploy_template({
  templateId: 2947,
  name: "My Weather to Slack",  // Custom name (optional)
  autoFix: true,  // Auto-fix common issues (default)
  autoUpgradeVersions: true  // Upgrade node versions (default)
});
// Returns: workflow ID, required credentials, fixes applied
```

---

## Self-Help Tools

### Get Tool Documentation

```javascript
// Overview of all tools
tools_documentation()

// Specific tool details
tools_documentation({
  topic: "search_nodes",
  depth: "full"
})

// Code node guides
tools_documentation({topic: "javascript_code_node_guide", depth: "full"})
tools_documentation({topic: "python_code_node_guide", depth: "full"})
```

### AI Agent Guide

```javascript
// Comprehensive AI workflow guide
ai_agents_guide()
// Returns: Architecture, connections, tools, validation, best practices
```

### Health Check

```javascript
// Quick health check
n8n_health_check()

// Detailed diagnostics
n8n_health_check({mode: "diagnostic"})
// → Returns: status, env vars, tool status, API connectivity
```

---

## Tool Availability

**Always Available** (no n8n API needed):
- search_nodes, get_node
- validate_node, validate_workflow
- search_templates, get_template
- tools_documentation, ai_agents_guide

**Requires n8n API** (N8N_API_URL + N8N_API_KEY):
- n8n_create_workflow
- n8n_update_partial_workflow
- n8n_validate_workflow (by ID)
- n8n_list_workflows, n8n_get_workflow
- n8n_test_workflow
- n8n_executions
- n8n_deploy_template
- n8n_workflow_versions
- n8n_autofix_workflow

If API tools unavailable, use templates and validation-only workflows.

---

## Unified Tool Reference

### get_node (Unified Node Information)

**Detail Levels** (mode="info", default):
- `minimal` (~200 tokens) - Basic metadata only
- `standard` (~1-2K tokens) - Essential properties + operations (RECOMMENDED)
- `full` (~3-8K tokens) - Complete schema (use sparingly)

**Operation Modes**:
- `info` (default) - Node schema with detail level
- `docs` - Readable markdown documentation
- `search_properties` - Find specific properties (use with propertyQuery)
- `versions` - List all versions with breaking changes
- `compare` - Compare two versions
- `breaking` - Show only breaking changes
- `migrations` - Show auto-migratable changes

```javascript
// Standard (recommended)
get_node({nodeType: "nodes-base.httpRequest"})

// Get documentation
get_node({nodeType: "nodes-base.webhook", mode: "docs"})

// Search for properties
get_node({nodeType: "nodes-base.httpRequest", mode: "search_properties", propertyQuery: "auth"})

// Check versions
get_node({nodeType: "nodes-base.executeWorkflow", mode: "versions"})
```

### validate_node (Unified Validation)

**Modes**:
- `full` (default) - Comprehensive validation with errors/warnings/suggestions
- `minimal` - Quick required fields check only

**Profiles** (for mode="full"):
- `minimal` - Very lenient
- `runtime` - Standard (default, recommended)
- `ai-friendly` - Balanced for AI workflows
- `strict` - Most thorough (production)

```javascript
// Full validation with runtime profile
validate_node({nodeType: "nodes-base.slack", config: {...}, profile: "runtime"})

// Quick required fields check
validate_node({nodeType: "nodes-base.webhook", config: {}, mode: "minimal"})
```

---

## Performance Characteristics

| Tool | Response Time | Payload Size |
|------|---------------|--------------|
| search_nodes | <20ms | Small |
| get_node (standard) | <10ms | ~1-2KB |
| get_node (full) | <100ms | 3-8KB |
| validate_node (minimal) | <50ms | Small |
| validate_node (full) | <100ms | Medium |
| validate_workflow | 100-500ms | Medium |
| n8n_create_workflow | 100-500ms | Medium |
| n8n_update_partial_workflow | 50-200ms | Small |
| n8n_deploy_template | 200-500ms | Medium |

---

## Best Practices

### Do
- Use `get_node({detail: "standard"})` for most use cases
- Specify validation profile explicitly (`profile: "runtime"`)
- Use smart parameters (`branch`, `case`) for clarity
- Include `intent` parameter in workflow updates
- Follow search → get_node → validate workflow
- Iterate workflows (avg 56s between edits)
- Validate after every significant change
- Use `includeExamples: true` for real configs
- Use `n8n_deploy_template` for quick starts

### Don't
- Use `detail: "full"` unless necessary (wastes tokens)
- Forget nodeType prefix (`nodes-base.*`)
- Skip validation profiles
- Try to build workflows in one shot (iterate!)
- Ignore auto-sanitization behavior
- Use full prefix (`n8n-nodes-base.*`) with search/validate tools
- Forget to activate workflows after building

---

## Summary

**Most Important**:
1. Use **get_node** with `detail: "standard"` (default) - covers 95% of use cases
2. nodeType formats differ: `nodes-base.*` (search/validate) vs `n8n-nodes-base.*` (workflows)
3. Specify **validation profiles** (`runtime` recommended)
4. Use **smart parameters** (`branch="true"`, `case=0`)
5. Include **intent parameter** in workflow updates
6. **Auto-sanitization** runs on ALL nodes during updates
7. Workflows can be **activated via API** (`activateWorkflow` operation)
8. Workflows are built **iteratively** (56s avg between edits)

**Common Workflow**:
1. search_nodes → find node
2. get_node → understand config
3. validate_node → check config
4. n8n_create_workflow → build
5. n8n_validate_workflow → verify
6. n8n_update_partial_workflow → iterate
7. activateWorkflow → go live!

For details, see:
- SEARCH_GUIDE.md - Node discovery
- VALIDATION_GUIDE.md - Configuration validation
- WORKFLOW_GUIDE.md - Workflow management

---

**Related Skills**:
- n8n Expression Syntax - Write expressions in workflow fields
- n8n Workflow Patterns - Architectural patterns from templates
- n8n Validation Expert - Interpret validation errors
- n8n Node Configuration - Operation-specific requirements
- n8n Code JavaScript - Write JavaScript in Code nodes
- n8n Code Python - Write Python in Code nodes

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: n8n-multi-instance
description: Select, verify, and safely switch n8n MCP instances across production, staging, teams, or clients, especially before credential writes.
risk: critical
source: https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-multi-instance
source_repo: czlonkowski/n8n-skills
source_type: community
date_added: "2026-07-21"
author: Romuald Czlonkowski
license: MIT
license_source: https://github.com/czlonkowski/n8n-skills/blob/main/LICENSE
---

# Working with multiple n8n instances over MCP

## When to Use

Use this skill whenever one MCP connection can target multiple n8n instances, before instance-specific reads or writes, and whenever results suggest the session is aimed at the wrong environment.

Resolve the target by stable instance ID, verify it with a read-only health check, and state the resolved environment before mutations. Require explicit confirmation for credential create/update/delete operations, never print secret values, and stop on ambiguous targeting rather than guessing.

When the `n8n_instances` tool is available, the user has **multi-instance mode** on: one MCP
connection can reach several n8n instances (e.g. `prod`, `staging`, or one per client/team).
Every other n8n tool (`n8n_get_workflow`, `n8n_list_workflows`, `n8n_update_partial_workflow`,
`n8n_manage_datatable`, `n8n_manage_credentials`, `n8n_executions`, `n8n_test_workflow`, …) runs
against **whichever instance this session is currently targeting**. There is no per-call instance
argument: you change the target only by switching. Target the wrong instance and a read returns the
wrong data and a write lands in the wrong place — usually with **no error** (the one exception is an
ambiguous credential write, which fails closed; see below). So target deliberately.

If the `n8n_instances` tool is **not** present, the account is single-instance: ignore this skill
and use the n8n tools directly.

## Golden rules

Six rules. Each prevents a class of silent misroute.

1. **Discover first.** Call `n8n_instances({mode:"list"})` before acting so you know the instance
   names and which one is `current`.
2. **Switch by name to your target** before doing work on a non-default instance:
   `n8n_instances({mode:"switch", name:"<instance name>"})`. The match is case-insensitive.
3. **Switch in its own turn.** Never put a `switch` and a dependent operation in the **same
   parallel tool-call batch**. Calls in one batch have no guaranteed order, so the dependent call
   can be resolved against the *previous* instance before the switch's session state is visible.
   Switch, let it return, *then* operate.
4. **Verify before high-stakes ops.** Immediately before creating/updating/deleting **credentials**
   (and before destructive workflow edits), confirm `current` is the instance you intend — primary
   check is `n8n_instances({mode:"list"})`. The system fail-closes only the *ambiguous* credential
   case (rule 6); an explicit switch to the **wrong** instance still writes there silently, so this
   check is on you.
5. **An unexpected `NOT_FOUND` is almost always a wrong-instance misroute, not a deletion.** Don't
   recreate the object. Re-check the current instance and retry (see Recovery).
6. **On `INSTANCE_AMBIGUOUS`, switch on *this* session, then retry.** The system is refusing to
   write a secret because this session never picked a target itself. Comply — run `switch` here to
   confirm the instance, then retry the write. Don't work around it or retry blindly.

## Core workflow

```
1. n8n_instances({mode:"list"})                      # see available[] + current + default
2. n8n_instances({mode:"switch", name:"prod"})       # bind THIS session to "prod"
   → returns { previous, current }; confirm current.name == "prod"
3. (do your work) n8n_list_workflows / n8n_get_workflow / n8n_manage_datatable / ...
4. Before a credential write or a delete:
   n8n_instances({mode:"list"})  → re-confirm current, THEN n8n_manage_credentials({action:"create", ...})
```

To move to another instance, just `switch` again. The whole session follows the switch.

## The `n8n_instances` tool

Two modes (`mode` is required and enum-validated):

- `{mode:"list"}` → `{ current, default, available }`, no side effects.
  - `current` and `default` are each one instance `{ id, name, url, isDefault }` (or `null`).
  - `available` is every instance, each with an extra `isCurrent` boolean. Match by **`name`**;
    never hard-code `id`.
- `{mode:"switch", name:"<name>"}` → `{ previous, current }`, and binds this session to the named
  instance. `name` is case-insensitive.

### Error envelope (from the `n8n_instances` tool)

Every error returns `{ error: "<CODE>", message, … }`. The ones you'll actually hit:

| Code | When | What to do |
|---|---|---|
| `UNKNOWN_INSTANCE` | `name` matches no instance | Pick a name from the `available` list in the error payload and retry. |
| `NAME_REQUIRED` | `switch` with no `name` | Re-call with a `name` (the error lists the valid ones in `available`). |
| `MULTI_INSTANCE_DISABLED` | multi-instance mode is off | There's nothing to switch; use the n8n tools directly. The user can enable it at the n8n-mcp dashboard. |
| `NO_SESSION` | the request has **neither** an MCP session id **nor** a credential id | A selection has nowhere to land. Reconnect / initialize a session, then switch. |
| `UNKNOWN_MODE` | `mode` wasn't `list`/`switch` | Use `list` or `switch`. |
| `INVALID_CONTEXT` | server-side metadata missing | A server bug, not your input — report it. |

> Instance names can never be `default`, `current`, `list`, or `switch` (reserved), so you'll never
> see an instance literally named after a mode or field.

### `INSTANCE_AMBIGUOUS` (from the credential-write path, not the tool)

A separate, higher-stakes error. It is **not** returned by `n8n_instances` — it's returned by the
server when you call `n8n_manage_credentials` to **create/update/delete** a credential and the target
instance is ambiguous: this session never switched on its own but inherited a switch made elsewhere
(a fan-out / reconnect), pointing at a **non-default** instance. Rather than risk writing a secret to
the wrong instance, the server **blocks the write** (it never reaches n8n, no quota is charged) and
returns:

```json
{
  "error": "INSTANCE_AMBIGUOUS",
  "message": "… the session issuing this request never switched there itself … Re-run n8n_instances({mode:\"switch\", name:\"…\"}) on this session to confirm the target …",
  "lastSelected": { "id": "…", "name": "…" },
  "default":      { "id": "…", "name": "…" }
}
```

**Fix:** decide which instance you actually want (`lastSelected` is the inherited switch, `default`
is the account default), run `n8n_instances({mode:"switch", name:"…"})` on **this** session, then
retry the write. See rule 6.

## How targeting behaves (mental model)

- A `switch` **binds this session** to the chosen instance. The binding **persists for the rest of
  the session and survives reconnects, idle, and backend deploys** (~24h, the MCP session lifetime)
  — you should not need to re-switch before every call.
- Other sessions / terminals are **independent**: switching here does not move them.
- One session targets **one instance at a time**. There is no per-call instance argument; you
  change the target only via `switch`.
- **Reads and non-credential writes** route to the currently-selected instance, silently — a
  misroute produces wrong data or a `NOT_FOUND`, not an error.
- **Credential writes are the one guarded case.** They route the same way, except the server
  fail-closes the *ambiguous* state (a session that never switched, recovered onto a non-default
  instance) with `INSTANCE_AMBIGUOUS`. This is a safety net, not a substitute for rule 4: an
  explicit switch to the wrong instance still writes there.
- **If your selected instance is deleted** (the user removes it mid-session), the next call silently
  falls back to your **default** instance — no error. So default's data appearing where you expected
  another instance's can look like "my data vanished." Re-list to see where you are.

## Recovery playbook

| Symptom | What it usually means | Do this |
|---|---|---|
| `INSTANCE_AMBIGUOUS` on a credential create/update/delete | This session never switched itself; the system won't guess which instance to write the secret to | Run `n8n_instances({mode:"switch", name:"<target>"})` on this session (the error names `lastSelected` and `default` — pick the one you want), then retry the write. Never retry blindly. |
| `NOT_FOUND` for a workflow/datatable/credential you **know exists** | You're pointed at the wrong instance — **not** that it was deleted | `n8n_instances({mode:"list"})` → check `current`. If it's not your target, `switch` and retry. **Do not recreate the object.** |
| A read returns **empty or unfamiliar** data | Wrong-instance read, or a silent fallback to `default` after your instance was deleted | `n8n_instances({mode:"list"})`, confirm `current`, switch if needed, re-read before drawing conclusions. |
| `UNKNOWN_INSTANCE` on `switch` | The `name` is wrong (typo, or you guessed) | Read the `available` names in the error and switch to one of those. Names are case-insensitive. |
| `n8n_health_check` reports an `instanceName` you didn't expect | This session is on a different instance than you think | `switch` to the intended instance, then proceed. |
| Repeated misroutes within one turn | You batched a `switch` with dependent work | Split them: `switch` alone, await the result, then operate one logical step at a time. |

After any recovery switch, sanity-check with `n8n_instances({mode:"list"})` (read `current`) as the
primary signal. `n8n_health_check` also returns the resolved instance under `details.instanceName`,
but it can be absent on some paths (legacy/chat), so treat it as a secondary confirmation.

## Credential operations (highest stakes)

Credentials hold live secrets, and a misrouted credential write puts a secret on the **wrong
instance**. The server protects the **ambiguous** case automatically — if this session never picked
a target and inherited a switch to a non-default instance, the write fails closed with
`INSTANCE_AMBIGUOUS` (rule 6) and never reaches n8n. But that net is narrow: a credential write on a
session that **did** switch goes through to whatever instance it switched to, with no second
guess. So:

- **Verify `current` immediately before** `n8n_manage_credentials` create/update/delete — call
  `n8n_instances({mode:"list"})` in the same short sequence, not 10 steps earlier where a later
  switch could have moved you.
- **On `INSTANCE_AMBIGUOUS`**, switch on this session to confirm the target, then retry — don't
  work around it.
- Credential **reads** (`action:"list"`/`"get"`/`"getSchema"`) are not gated and don't write a
  secret, but a read off the wrong instance returns the wrong schema or list — so still verify
  `current` if the result looks wrong.
- For the `n8n_manage_credentials` tool itself (CRUD shapes, `getSchema` discovery, never inlining
  secrets into text fields), see `n8n-mcp-tools-expert`.

## Common multi-instance task: copy something between instances

To recreate a credential or workflow from instance A on instance B:

```
1. switch → A;  read the source (n8n_manage_credentials get / n8n_get_workflow)
2. switch → B   (its own call — never batched with the create below)
3. n8n_instances({mode:"list"})  → confirm current == B
4. create on B  (n8n_manage_credentials create / n8n_create_workflow)
```

Do each instance's steps in its own turn; never overlap `switch → B` with the create-on-B call
(rule 3), and switch explicitly on this session before the credential write so it isn't ambiguous
(rules 4 and 6).

## Quick reference

- See instances + where you are: `n8n_instances({mode:"list"})` → `{ current, default, available }`
- Change target: `n8n_instances({mode:"switch", name:"<name>"})` — its own turn, then operate
- Confirm target: `current` from `list` (primary); `details.instanceName` from `n8n_health_check` (secondary, may be absent)
- `UNKNOWN_INSTANCE` → switch to a name from the error's `available` list, then retry
- `INSTANCE_AMBIGUOUS` (credential write) → `switch` on this session to confirm the target, then retry
- Unexpected `NOT_FOUND` → verify the instance, switch, retry; **do not recreate**
- Before credential writes → re-`list`, confirm `current`, then write (the fail-close only covers the ambiguous case)

## Integration with other skills

- **n8n-mcp-tools-expert** — owns `n8n_manage_credentials` (CRUD + `getSchema`) and the rule that
  secrets go through the credential system, never text fields. This skill adds the "which instance?"
  layer on top.
- **using-n8n-mcp-skills** — the router; consult it for which skill owns a given build step.

## Limitations

- Instance discovery and switching depend on the connected n8n MCP server exposing multi-instance tools.
- A successful switch does not authorize mutations or prove that the selected environment is appropriate for the task.
- Unexpected empty or missing data may have causes other than misrouting; verify before changing targets.


---
name: n8n-node-configuration
description: Operation-aware node configuration guidance. Use when configuring nodes, understanding property dependencies, determining required fields, choosing between get_node detail levels, or learning common configuration patterns by node type.
risk: critical
source: community
---

# n8n Node Configuration

Expert guidance for operation-aware node configuration with property dependencies.

## When to Use
- You need to configure an n8n node correctly for a specific resource and operation.
- The task involves required fields, property dependencies, or choosing the right `get_node` detail level.
- You are troubleshooting node setup rather than overall workflow architecture.

---

## Configuration Philosophy

**Progressive disclosure**: Start minimal, add complexity as needed

Configuration best practices:
- `get_node` with `detail: "standard"` is the most used discovery pattern
- 56 seconds average between configuration edits
- Covers 95% of use cases with 1-2K tokens response

**Key insight**: Most configurations need only standard detail, not full schema!

---

## Core Concepts

### 1. Operation-Aware Configuration

**Not all fields are always required** - it depends on operation!

**Example**: Slack node
```javascript
// For operation='post'
{
  "resource": "message",
  "operation": "post",
  "channel": "#general",  // Required for post
  "text": "Hello!"        // Required for post
}

// For operation='update'
{
  "resource": "message",
  "operation": "update",
  "messageId": "123",     // Required for update (different!)
  "text": "Updated!"      // Required for update
  // channel NOT required for update
}
```

**Key**: Resource + operation determine which fields are required!

### 2. Property Dependencies

**Fields appear/disappear based on other field values**

**Example**: HTTP Request node
```javascript
// When method='GET'
{
  "method": "GET",
  "url": "https://api.example.com"
  // sendBody not shown (GET doesn't have body)
}

// When method='POST'
{
  "method": "POST",
  "url": "https://api.example.com",
  "sendBody": true,       // Now visible!
  "body": {               // Required when sendBody=true
    "contentType": "json",
    "content": {...}
  }
}
```

**Mechanism**: displayOptions control field visibility

### 3. Progressive Discovery

**Use the right detail level**:

1. **get_node({detail: "standard"})** - DEFAULT
   - Quick overview (~1-2K tokens)
   - Required fields + common options
   - **Use first** - covers 95% of needs

2. **get_node({mode: "search_properties", propertyQuery: "..."})** (for finding specific fields)
   - Find properties by name
   - Use when looking for auth, body, headers, etc.

3. **get_node({detail: "full"})** (complete schema)
   - All properties (~3-8K tokens)
   - Use only when standard detail is insufficient

---

## Configuration Workflow

### Standard Process

```
1. Identify node type and operation
   ↓
2. Use get_node (standard detail is default)
   ↓
3. Configure required fields
   ↓
4. Validate configuration
   ↓
5. If field unclear → get_node({mode: "search_properties"})
   ↓
6. Add optional fields as needed
   ↓
7. Validate again
   ↓
8. Deploy
```

### Example: Configuring HTTP Request

**Step 1**: Identify what you need
```javascript
// Goal: POST JSON to API
```

**Step 2**: Get node info
```javascript
const info = get_node({
  nodeType: "nodes-base.httpRequest"
});

// Returns: method, url, sendBody, body, authentication required/optional
```

**Step 3**: Minimal config
```javascript
{
  "method": "POST",
  "url": "https://api.example.com/create",
  "authentication": "none"
}
```

**Step 4**: Validate
```javascript
validate_node({
  nodeType: "nodes-base.httpRequest",
  config,
  profile: "runtime"
});
// → Error: "sendBody required for POST"
```

**Step 5**: Add required field
```javascript
{
  "method": "POST",
  "url": "https://api.example.com/create",
  "authentication": "none",
  "sendBody": true
}
```

**Step 6**: Validate again
```javascript
validate_node({...});
// → Error: "body required when sendBody=true"
```

**Step 7**: Complete configuration
```javascript
{
  "method": "POST",
  "url": "https://api.example.com/create",
  "authentication": "none",
  "sendBody": true,
  "body": {
    "contentType": "json",
    "content": {
      "name": "={{$json.name}}",
      "email": "={{$json.email}}"
    }
  }
}
```

**Step 8**: Final validation
```javascript
validate_node({...});
// → Valid! ✅
```

---

## get_node Detail Levels

### Standard Detail (DEFAULT - Use This!)

**✅ Starting configuration**
```javascript
get_node({
  nodeType: "nodes-base.slack"
});
// detail="standard" is the default
```

**Returns** (~1-2K tokens):
- Required fields
- Common options
- Operation list
- Metadata

**Use**: 95% of configuration needs

### Full Detail (Use Sparingly)

**✅ When standard isn't enough**
```javascript
get_node({
  nodeType: "nodes-base.slack",
  detail: "full"
});
```

**Returns** (~3-8K tokens):
- Complete schema
- All properties
- All nested options

**Warning**: Large response, use only when standard insufficient

### Search Properties Mode

**✅ Looking for specific field**
```javascript
get_node({
  nodeType: "nodes-base.httpRequest",
  mode: "search_properties",
  propertyQuery: "auth"
});
```

**Use**: Find authentication, headers, body fields, etc.

### Decision Tree

```
┌─────────────────────────────────┐
│ Starting new node config?       │
├─────────────────────────────────┤
│ YES → get_node (standard)       │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│ Standard has what you need?     │
├─────────────────────────────────┤
│ YES → Configure with it         │
│ NO  → Continue                  │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│ Looking for specific field?     │
├─────────────────────────────────┤
│ YES → search_properties mode    │
│ NO  → Continue                  │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│ Still need more details?        │
├─────────────────────────────────┤
│ YES → get_node({detail: "full"})│
└─────────────────────────────────┘
```

---

## Property Dependencies Deep Dive

### displayOptions Mechanism

**Fields have visibility rules**:

```javascript
{
  "name": "body",
  "displayOptions": {
    "show": {
      "sendBody": [true],
      "method": ["POST", "PUT", "PATCH"]
    }
  }
}
```

**Translation**: "body" field shows when:
- sendBody = true AND
- method = POST, PUT, or PATCH

### Common Dependency Patterns

#### Pattern 1: Boolean Toggle

**Example**: HTTP Request sendBody
```javascript
// sendBody controls body visibility
{
  "sendBody": true   // → body field appears
}
```

#### Pattern 2: Operation Switch

**Example**: Slack resource/operation
```javascript
// Different operations → different fields
{
  "resource": "message",
  "operation": "post"
  // → Shows: channel, text, attachments, etc.
}

{
  "resource": "message",
  "operation": "update"
  // → Shows: messageId, text (different fields!)
}
```

#### Pattern 3: Type Selection

**Example**: IF node conditions
```javascript
{
  "type": "string",
  "operation": "contains"
  // → Shows: value1, value2
}

{
  "type": "boolean",
  "operation": "equals"
  // → Shows: value1, value2, different operators
}
```

### Finding Property Dependencies

**Use get_node with search_properties mode**:
```javascript
get_node({
  nodeType: "nodes-base.httpRequest",
  mode: "search_properties",
  propertyQuery: "body"
});

// Returns property paths matching "body" with descriptions
```

**Or use full detail for complete schema**:
```javascript
get_node({
  nodeType: "nodes-base.httpRequest",
  detail: "full"
});

// Returns complete schema with displayOptions rules
```

**Use this when**: Validation fails and you don't understand why field is missing/required

---

## Common Node Patterns

### Pattern 1: Resource/Operation Nodes

**Examples**: Slack, Google Sheets, Airtable

**Structure**:
```javascript
{
  "resource": "<entity>",      // What type of thing
  "operation": "<action>",     // What to do with it
  // ... operation-specific fields
}
```

**How to configure**:
1. Choose resource
2. Choose operation
3. Use get_node to see operation-specific requirements
4. Configure required fields

### Pattern 2: HTTP-Based Nodes

**Examples**: HTTP Request, Webhook

**Structure**:
```javascript
{
  "method": "<HTTP_METHOD>",
  "url": "<endpoint>",
  "authentication": "<type>",
  // ... method-specific fields
}
```

**Dependencies**:
- POST/PUT/PATCH → sendBody available
- sendBody=true → body required
- authentication != "none" → credentials required

### Pattern 3: Database Nodes

**Examples**: Postgres, MySQL, MongoDB

**Structure**:
```javascript
{
  "operation": "<query|insert|update|delete>",
  // ... operation-specific fields
}
```

**Dependencies**:
- operation="executeQuery" → query required
- operation="insert" → table + values required
- operation="update" → table + values + where required

### Pattern 4: Conditional Logic Nodes

**Examples**: IF, Switch, Merge

**Structure**:
```javascript
{
  "conditions": {
    "<type>": [
      {
        "operation": "<operator>",
        "value1": "...",
        "value2": "..."  // Only for binary operators
      }
    ]
  }
}
```

**Dependencies**:
- Binary operators (equals, contains, etc.) → value1 + value2
- Unary operators (isEmpty, isNotEmpty) → value1 only + singleValue: true

---

## Operation-Specific Configuration

### Slack Node Examples

#### Post Message
```javascript
{
  "resource": "message",
  "operation": "post",
  "channel": "#general",      // Required
  "text": "Hello!",           // Required
  "attachments": [],          // Optional
  "blocks": []                // Optional
}
```

#### Update Message
```javascript
{
  "resource": "message",
  "operation": "update",
  "messageId": "1234567890",  // Required (different from post!)
  "text": "Updated!",         // Required
  "channel": "#general"       // Optional (can be inferred)
}
```

#### Create Channel
```javascript
{
  "resource": "channel",
  "operation": "create",
  "name": "new-channel",      // Required
  "isPrivate": false          // Optional
  // Note: text NOT required for this operation
}
```

### HTTP Request Node Examples

#### GET Request
```javascript
{
  "method": "GET",
  "url": "https://api.example.com/users",
  "authentication": "predefinedCredentialType",
  "nodeCredentialType": "httpHeaderAuth",
  "sendQuery": true,                    // Optional
  "queryParameters": {                  // Shows when sendQuery=true
    "parameters": [
      {
        "name": "limit",
        "value": "100"
      }
    ]
  }
}
```

#### POST with JSON
```javascript
{
  "method": "POST",
  "url": "https://api.example.com/users",
  "authentication": "none",
  "sendBody": true,                     // Required for POST
  "body": {                             // Required when sendBody=true
    "contentType": "json",
    "content": {
      "name": "John Doe",
      "email": "john@example.com"
    }
  }
}
```

### IF Node Examples

#### String Comparison (Binary)
```javascript
{
  "conditions": {
    "string": [
      {
        "value1": "={{$json.status}}",
        "operation": "equals",
        "value2": "active"              // Binary: needs value2
      }
    ]
  }
}
```

#### Empty Check (Unary)
```javascript
{
  "conditions": {
    "string": [
      {
        "value1": "={{$json.email}}",
        "operation": "isEmpty",
        // No value2 - unary operator
        "singleValue": true             // Auto-added by sanitization
      }
    ]
  }
}
```

---

## Handling Conditional Requirements

### Example: HTTP Request Body

**Scenario**: body field required, but only sometimes

**Rule**:
```
body is required when:
  - sendBody = true AND
  - method IN (POST, PUT, PATCH, DELETE)
```

**How to discover**:
```javascript
// Option 1: Read validation error
validate_node({...});
// Error: "body required when sendBody=true"

// Option 2: Search for the property
get_node({
  nodeType: "nodes-base.httpRequest",
  mode: "search_properties",
  propertyQuery: "body"
});
// Shows: body property with displayOptions rules

// Option 3: Try minimal config and iterate
// Start without body, validation will tell you if needed
```

### Example: IF Node singleValue

**Scenario**: singleValue property appears for unary operators

**Rule**:
```
singleValue should be true when:
  - operation IN (isEmpty, isNotEmpty, true, false)
```

**Good news**: Auto-sanitization fixes this!

**Manual check**:
```javascript
get_node({
  nodeType: "nodes-base.if",
  detail: "full"
});
// Shows complete schema with operator-specific rules
```

---

## Configuration Anti-Patterns

### ❌ Don't: Over-configure Upfront

**Bad**:
```javascript
// Adding every possible field
{
  "method": "GET",
  "url": "...",
  "sendQuery": false,
  "sendHeaders": false,
  "sendBody": false,
  "timeout": 10000,
  "ignoreResponseCode": false,
  // ... 20 more optional fields
}
```

**Good**:
```javascript
// Start minimal
{
  "method": "GET",
  "url": "...",
  "authentication": "none"
}
// Add fields only when needed
```

### ❌ Don't: Skip Validation

**Bad**:
```javascript
// Configure and deploy without validating
const config = {...};
n8n_update_partial_workflow({...});  // YOLO
```

**Good**:
```javascript
// Validate before deploying
const config = {...};
const result = validate_node({...});
if (result.valid) {
  n8n_update_partial_workflow({...});
}
```

### ❌ Don't: Ignore Operation Context

**Bad**:
```javascript
// Same config for all Slack operations
{
  "resource": "message",
  "operation": "post",
  "channel": "#general",
  "text": "..."
}

// Then switching operation without updating config
{
  "resource": "message",
  "operation": "update",  // Changed
  "channel": "#general",  // Wrong field for update!
  "text": "..."
}
```

**Good**:
```javascript
// Check requirements when changing operation
get_node({
  nodeType: "nodes-base.slack"
});
// See what update operation needs (messageId, not channel)
```

---

## Best Practices

### ✅ Do

1. **Start with get_node (standard detail)**
   - ~1-2K tokens response
   - Covers 95% of configuration needs
   - Default detail level

2. **Validate iteratively**
   - Configure → Validate → Fix → Repeat
   - Average 2-3 iterations is normal
   - Read validation errors carefully

3. **Use search_properties mode when stuck**
   - If field seems missing, search for it
   - Understand what controls field visibility
   - `get_node({mode: "search_properties", propertyQuery: "..."})`

4. **Respect operation context**
   - Different operations = different requirements
   - Always check get_node when changing operation
   - Don't assume configs are transferable

5. **Trust auto-sanitization**
   - Operator structure fixed automatically
   - Don't manually add/remove singleValue
   - IF/Switch metadata added on save

### ❌ Don't

1. **Jump to detail="full" immediately**
   - Try standard detail first
   - Only escalate if needed
   - Full schema is 3-8K tokens

2. **Configure blindly**
   - Always validate before deploying
   - Understand why fields are required
   - Use search_properties for conditional fields

3. **Copy configs without understanding**
   - Different operations need different fields
   - Validate after copying
   - Adjust for new context

4. **Manually fix auto-sanitization issues**
   - Let auto-sanitization handle operator structure
   - Focus on business logic
   - Save and let system fix structure

---

## Detailed References

For comprehensive guides on specific topics:

- **DEPENDENCIES.md** - Deep dive into property dependencies and displayOptions
- **OPERATION_PATTERNS.md** - Common configuration patterns by node type

---

## Summary

**Configuration Strategy**:
1. Start with `get_node` (standard detail is default)
2. Configure required fields for operation
3. Validate configuration
4. Search properties if stuck
5. Iterate until valid (avg 2-3 cycles)
6. Deploy with confidence

**Key Principles**:
- **Operation-aware**: Different operations = different requirements
- **Progressive disclosure**: Start minimal, add as needed
- **Dependency-aware**: Understand field visibility rules
- **Validation-driven**: Let validation guide configuration

**Related Skills**:
- **n8n MCP Tools Expert** - How to use discovery tools correctly
- **n8n Validation Expert** - Interpret validation errors
- **n8n Expression Syntax** - Configure expression fields
- **n8n Workflow Patterns** - Apply patterns with proper configuration

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: n8n-subworkflows
description: Build reusable n8n sub-workflows with typed inputs, all-vs-each execution, discoverable naming, and agent-tool exposure.
risk: critical
source: https://github.com/czlonkowski/n8n-skills/tree/main/skills/n8n-subworkflows
source_repo: czlonkowski/n8n-skills
source_type: community
date_added: "2026-07-21"
author: Romuald Czlonkowski
license: MIT
license_source: https://github.com/czlonkowski/n8n-skills/blob/main/LICENSE
---

# n8n Sub-workflows

## When to Use

Use this skill when shared or multi-step logic should become a typed reusable workflow, when an existing workflow is growing difficult to reason about, or when an agent needs a workflow exposed as a tool.

Preserve authentication and authorization boundaries when extracting logic. Do not place credentials in inputs or returned data, declare state-changing behavior explicitly, and ask before running or activating a sub-workflow that sends, writes, deletes, or calls a billable external service.

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.

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.

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.

---

## The two non-negotiables

Everything else is judgement. These two are not.

### 1. Search before you build

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**:

```
n8n_list_workflows()                          # scan the library
n8n_get_workflow({ id: "<candidate>" })       # read its inputs/outputs + body
```

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 **references/NAMING_AND_DISCOVERY.md**.

### 2. The Execute Workflow Trigger uses "Define Below" with typed fields — not passthrough

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.

Two exceptions, and only two:

- **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.
- **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.

Outside those two cases, passthrough is a bug. See "Inputs and outputs as a contract" below.

---

## Should this be a sub-workflow?

You're about to write a chunk of logic. Run it through this:

```
Could this plausibly be needed in another workflow?
  └─ Yes → extract.

Is it a generic concern (auth, retry, parsing, formatting, ID generation)?
  └─ Almost always → extract. These are the canonical reusable sub-workflows.

Is it >5 nodes and conceptually one thing?
  └─ Probably extract, even if reuse isn't certain. It's better isolated.

Is it one HTTP call with no logic around it?
  └─ Don't. A sub-workflow that's just trigger → HTTP → return adds a boundary
     for nothing.

Is it tightly coupled to this one caller's data shape?
  └─ Don't extract yet — fix the data shape first, or you just relocate the coupling.
```

The reasons to extract go beyond reuse:

- **Readability.** The caller shows one node ("Parse date") instead of five.
- **Testability.** Run the sub-workflow alone with pinned input (`n8n_test_workflow`).
- **Replaceability.** Swap the implementation without rippling to callers.

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.

---

## Stateless vs. stateful (deliberately)

Both are first-class. The choice is about intent and what the contract promises.

**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.

- `Subworkflow: Parse RFC2822 date` — date string → ISO date or error.
- `Subworkflow: Compute MRR from subscription` — subscription object → number.
- `Subworkflow: Format invoice as HTML` — invoice data → HTML string.

**Stateful (deliberate)** — reads or writes external state *behind a clean contract*. This is the repository pattern: the sub-workflow abstracts the storage operation so callers think in domain terms, not SQL.

- `Customer: get by id` — id → customer object or `{ ok: false, error: "not_found" }`. Reads the DB.
- `Customer: write billing record` — record → `{ ok: true, id }`. Writes the DB.
- `Notify: send to on-call` — channel, message → `{ ok: true, messageId }`. Calls Slack/SMTP.

Why build these as sub-workflows: callers think `get customer by id` instead of writing the query; you can swap the store (Postgres → Supabase, native node → HTTP) without touching a single caller; and idempotency, retry, and validation get centralized in one place.

What to avoid is **accidental state** — a sub-workflow named and described as pure that quietly writes to a log table. That ambushes every caller who reasonably assumed it was safe to retry or compose. Either make the side effect part of the contract (rename it, document it, return its result) or move it out.

---

## Inputs and outputs as a contract

The trigger's declared fields and the last node's output shape *are* the sub-workflow's API. Treat them like one.

### Declaring typed inputs (Define Below)

Each declared input is a typed parameter the caller fills. Pick types deliberately (`string`, `number`, `boolean`, `array`, `object`) — an agent uses these as the required types when filling tool parameters, and humans rely on them when wiring callers. The trigger node parameters look like this:

```json
{
  "type": "n8n-nodes-base.executeWorkflowTrigger",
  "parameters": {
    "workflowInputs": {
      "values": [
        { "name": "list_of_ids",        "type": "array" },
        { "name": "include_transcript", "type": "boolean" },
        { "name": "session_id",          "type": "string" }
      ]
    }
  }
}
```

Inside the body, read them as `$json.list_of_ids`, or from anywhere downstream as `$('When Executed by Another Workflow').first().json.<field>` (see **n8n-expression-syntax**).

### The contract rules

- **Document inputs and outputs in the workflow `description`.** Field names, types, purpose, and a few representative keywords. The description is what callers (human and agent) read for the contract, and it's what `n8n_list_workflows` matches against.
- **Return consistent, natural shapes — not storage shapes.** A sub-workflow that owns a Data Table or an S3 file hides that representation from callers. Arrays return as arrays, objects as objects, dates as ISO strings — regardless of whether the underlying storage was JSON-stringified text. The return contract is the *interface*; the storage layout is *implementation detail*. Common slip: a sub-workflow with a "fresh" path (just-computed, natural shape) and a "cached" path (just read from a stringified column). Wrong instinct: stringify the fresh path to match the cached one. Right instinct: parse the cached path so both return the natural shape.
- **Return errors, don't always throw.** For *expected* failures (a parse error, a not-found), return `{ ok: false, error: "..." }` so the caller can branch without wiring an error output. Reserve throwing for genuinely unexpected failures — see **n8n-error-handling**.
- **The contract is frozen once it has callers.** Adding *optional* fields is safe. Renaming or removing a field is dangerous: n8n won't error on an unrecognized input field — the body just sees `undefined`, the caller has no idea, and you get a silent contract break. To change a field, enumerate every caller (`n8n_list_workflows` + inspect each one's Execute Workflow node), migrate them in the same change, and verify with `validate_workflow` and `n8n_get_workflow` before you're done.

### The final Return node — the legitimate Set exception

Shape the output with a final **Set / Edit Fields** node, named `Return` or `Return <thing>`. This is the one place a Set node earns its keep against the usual "don't add a trailing Set node" advice from **n8n-expression-syntax**: the implicit consumer of a sub-workflow's last node is *every caller*, so an explicit Set makes the return contract visible — a reader sees the whole API by reading one node, and you strip any noise fields the last computation node carried.

---

## Calling sub-workflows: `mode` and `waitForSubWorkflow`

Two settings on the caller's **Execute Workflow** node decide how the sub-workflow runs.

### `mode`: `all` vs `each`

| `mode` | Sub-workflow runs | Items per run |
|---|---|---|
| `all` (default) | once | all N items (flowing per-item through nodes as usual) |
| `each` | N times | exactly one item per run |

For a body that just processes items the normal way, the two are equivalent — n8n nodes iterate per-item either way. **The split only matters when the body assumes it sees exactly one item**: a per-run aggregation, "this is THE customer to act on" logic, or a final write that should fire once per input. With `all`, that body gets all N items at once and the assumption breaks (you aggregate everyone into one result instead of one-per-input). With `each`, each invocation gets one item and the assumption holds.

So: when you need per-item iteration, prefer `mode: each` over dropping a Loop Over Items node *inside* the sub-workflow. The mode does the iteration for you, and the body stays simple and single-item.

### `waitForSubWorkflow`: `true` vs `false`

`waitForSubWorkflow` defaults to `true` — the caller blocks until the sub-workflow returns, then continues with its output. Set `options.waitForSubWorkflow: false` to fire-and-forget: the call dispatches, the caller moves on immediately, the sub-workflow runs in the background, and downstream sees no return data.

### The only true parallelization n8n offers

`mode: each` + `waitForSubWorkflow: false` is **the only way to get genuinely concurrent sub-workflow execution**: N items dispatch N runs that execute in parallel (still bounded by per-instance concurrency limits). The caller doesn't know when — or whether — any of them finished, so it's only useful with a separate completion-tracking mechanism, typically a Data Table the sub-workflow updates as it progresses. The full stage → dispatch → poll pattern is in **references/SUBWORKFLOW_PATTERNS.md** ("Fire-and-forget parallelization").

---

## Splitting by input shape (the N+1 pattern)

When a sub-workflow has multiple input paths whose contracts *genuinely* differ — binary vs JSON, sync vs async, divergent auth schemes — don't cram them under one trigger with passthrough + an internal Switch. The forcing function is real: passthrough (for binary or zero-input) and Define Below (for typed inputs) are mutually exclusive on a single trigger. The reflex to "pick passthrough because it's most permissive, then branch inside" costs you the typed schema (no clean agent tool), grows branch-shape cruft, and turns every new input shape into more branching.

The fix: for N divergent input contracts, build **N+1 sub-workflows** — one outer per contract, each doing its input-specific prep (validation, fetching, hashing, extraction) and calling **one shared downstream** sub-workflow with a normalized shape. The shared core has a single typed input contract and knows nothing about which outer called it. The worked example (process a paper from an external ID *or* an uploaded PDF) is in **references/SUBWORKFLOW_PATTERNS.md**.

---

## Sub-workflow as an agent tool

A sub-workflow with a typed Define Below trigger doubles as an AI-agent tool: the agent fills the declared fields via `$fromAI`, the body runs, the result comes back as the tool observation. This is the high-value reason to default to Define Below — passthrough triggers can't expose a fill-able schema.

The zero-input case still works as a tool: the agent's only decision is whether to invoke. The binary case does *not* wire cleanly as a tool, because agents can't pass binary directly.

For tool naming, descriptions, and the binary-input workaround, see **n8n-agents**; for the binary handling itself, **n8n-binary-and-data**.

---

## Anti-patterns

| Anti-pattern | What goes wrong | Fix |
|---|---|---|
| Duplicating the same logic in three workflows | A bug gets fixed in two places, the third drifts | Extract once to a named sub-workflow |
| Building a new sub-workflow without searching | The library grows duplicates; future searches find both | `n8n_list_workflows` / `n8n_get_workflow` first |
| Trigger set to passthrough when not handling binary and not zero-input | No schema → agents can't fill params, structured callers can't bind | Use Define Below with typed `workflowInputs.values` |
| Zero-input passthrough with no clear-and-document | Body silently reads stray fields from whatever the caller forwarded | Start with a Set ("Keep Only Set", no fields) and a sticky noting "no inputs expected" |
| Sub-workflow named/described as pure that quietly writes state | Callers can't reason about retry/idempotency; the side effect ambushes them | Make the side effect part of the contract, or move it out |
| Sub-workflow with no `description` | Won't be found in future searches; nobody knows what it does | Set `description` with input/output shape + keywords |
| Name like `Helper 3` / no prefix | Doesn't say what it does, matches no prefix search | Verb-first prefix (`Subworkflow:`, `<Domain>:`, `Tool:`) |
| `mode: all` on a body that assumes one item | Aggregates all inputs into one result instead of one-per-input | `mode: each` (and skip the internal Loop Over Items) |
| Renaming a live input field without migrating callers | Callers send the old name → body sees `undefined`, no error anywhere | Migrate every caller in the same change; verify with `validate_workflow` |
| 30-node workflow with no extraction | Hard to read, test, and replace | Extract logical sections into sub-workflows |

---

## What's NOT available via the community MCP

| Want to do | Reality |
|---|---|
| Filter/discover workflows by **tag** | The MCP can't read or filter by tags (UI-only). Discovery is the *name* — use verb-first prefixes and `n8n_list_workflows`. |
| Catch an **unrecognized input field** | n8n doesn't error on one. The body sees `undefined` and the caller never knows — a silent contract break. Verify field renames by hand across callers. |
| Set the input mode / fields without a typed trigger | The trigger node itself must declare `workflowInputs.values`. Configure it with `n8n_update_partial_workflow` (`updateNode` / `patchNodeField`); validate with `get_node` / `validate_node`. |

What the MCP **can** do: build the sub-workflow and its callers (`n8n_update_partial_workflow` with `addNode` / `addConnection` / `updateNode` / `patchNodeField`), discover existing ones (`n8n_list_workflows`, `n8n_get_workflow`), validate (`validate_workflow`, `n8n_validate_workflow`), test in isolation (`n8n_test_workflow`), inspect runs (`n8n_executions`), back a stateful sub-workflow with a Data Table (`n8n_manage_datatable`), and activate (`activateWorkflow`).

---

## Reference files

| File | Read when |
|---|---|
| **references/SUBWORKFLOW_PATTERNS.md** | `mode: all` vs `each` in depth, splitting by input shape (the N+1 worked example), fire-and-forget parallelization with Data Table polling |
| **references/NAMING_AND_DISCOVERY.md** | Naming a new sub-workflow, the verb-first prefix convention, searching for existing ones, writing a discoverable description |

---

## Integration with other skills

- **n8n-workflow-patterns** — use it for the overall shape of the orchestrating workflow; use this skill to decide which sections become sub-workflows.
- **n8n-mcp-tools-expert** — parameter formats for `n8n_list_workflows`, `n8n_get_workflow`, `n8n_update_partial_workflow`, and `n8n_manage_datatable` (the Data Table behind a stateful sub-workflow and the fire-and-forget poll).
- **n8n-node-configuration** — `workflowInputs` and the `inputSource` (Define Below vs 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 both live here.
- **n8n-error-handling** — expected failures return `{ ok: false, error }`; unexpected ones throw and route through error outputs. A sub-workflow boundary is a natural place to define that line.
- **n8n-validation-expert** — validate the sub-workflow and its callers; an unrecognized input field won't surface here, so verify field changes manually.
- **n8n-code-javascript / n8n-code-python** — when a sub-workflow's body is a single Code node, its contract is still the trigger's typed inputs and the returned shape, not the Code node's internals.
- **n8n-code-tool** — the Custom Code Tool is the *inline* agent-tool option; a sub-workflow tool is the reusable, multi-step one. Pick the sub-workflow when the logic is shared across agents or needs the full Code-node sandbox.
- **n8n-agents** — wiring a typed sub-workflow as an agent tool, including the zero-input and binary cases.
- **n8n-binary-and-data** — passthrough triggers for binary input, and why binary can't flow through an agent tool directly.
- **using-n8n-mcp-skills** — when to consult which skill across a build.

---

## Quick reference checklist

Before shipping a sub-workflow:

- [ ] **Searched first** with `n8n_list_workflows` / `n8n_get_workflow` — it doesn't already exist
- [ ] **Trigger uses Define Below** with typed `workflowInputs.values` (unless binary or zero-input)
- [ ] **Zero-input passthrough** (if used) starts with a "Keep Only Set" Set node + a sticky noting no inputs
- [ ] **Name** has a verb-first prefix (`Subworkflow:`, `<Domain>:`, `Tool:`)
- [ ] **Description** documents input/output shape and carries searchable keywords
- [ ] **Returns a natural, consistent shape** via a final `Return` Set node — not a storage shape
- [ ] **Expected failures** return `{ ok: false, error }`; only unexpected ones throw
- [ ] **Caller `mode`** is `each` if the body assumes a single item (not an internal Loop Over Items)
- [ ] **`waitForSubWorkflow`** is set deliberately (`false` only with a completion-tracking mechanism)
- [ ] **Stateful sub-workflows** declare their side effect in name + description — no accidental state
- [ ] **Validated** with `validate_workflow`; tested in isolation with `n8n_test_workflow`

---

**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. A passthrough trigger that isn't for binary or a zero-arg op, or a name nobody can search, is how a reusable function quietly becomes the next duplicate.

## Limitations

- Validation does not detect every caller contract mismatch, side effect, or item-linking error.
- Tags and some workflow settings remain UI-only and may not be discoverable through the connected MCP server.
- Refactoring shared logic requires checking every caller; this skill cannot prove that external callers were migrated.


---
name: n8n-validation-expert
description: "Expert guide for interpreting and fixing n8n validation errors."
risk: critical
source: community
---

# n8n Validation Expert

Expert guide for interpreting and fixing n8n validation errors.

## When to Use
- You need to interpret or fix validation errors in an n8n workflow.
- The task involves `missing_required`, `invalid_value`, expression failures, or iterative validate-fix loops.
- You want concrete remediation guidance for workflow validation output.

---

## Validation Philosophy

**Validate early, validate often**

Validation is typically iterative:
- Expect validation feedback loops
- Usually 2-3 validate → fix cycles
- Average: 23s thinking about errors, 58s fixing them

**Key insight**: Validation is an iterative process, not one-shot!

---

## Error Severity Levels

### 1. Errors (Must Fix)
**Blocks workflow execution** - Must be resolved before activation

**Types**:
- `missing_required` - Required field not provided
- `invalid_value` - Value doesn't match allowed options
- `type_mismatch` - Wrong data type (string instead of number)
- `invalid_reference` - Referenced node doesn't exist
- `invalid_expression` - Expression syntax error

**Example**:
```json
{
  "type": "missing_required",
  "property": "channel",
  "message": "Channel name is required",
  "fix": "Provide a channel name (lowercase, no spaces, 1-80 characters)"
}
```

### 2. Warnings (Should Fix)
**Doesn't block execution** - Workflow can be activated but may have issues

**Types**:
- `best_practice` - Recommended but not required
- `deprecated` - Using old API/feature
- `performance` - Potential performance issue

**Example**:
```json
{
  "type": "best_practice",
  "property": "errorHandling",
  "message": "Slack API can have rate limits",
  "suggestion": "Add onError: 'continueRegularOutput' with retryOnFail"
}
```

### 3. Suggestions (Optional)
**Nice to have** - Improvements that could enhance workflow

**Types**:
- `optimization` - Could be more efficient
- `alternative` - Better way to achieve same result

---

## The Validation Loop

### Pattern from Telemetry
**7,841 occurrences** of this pattern:

```
1. Configure node
   ↓
2. validate_node (23 seconds thinking about errors)
   ↓
3. Read error messages carefully
   ↓
4. Fix errors
   ↓
5. validate_node again (58 seconds fixing)
   ↓
6. Repeat until valid (usually 2-3 iterations)
```

### Example
```javascript
// Iteration 1
let config = {
  resource: "channel",
  operation: "create"
};

const result1 = validate_node({
  nodeType: "nodes-base.slack",
  config,
  profile: "runtime"
});
// → Error: Missing "name"

// ⏱  23 seconds thinking...

// Iteration 2
config.name = "general";

const result2 = validate_node({
  nodeType: "nodes-base.slack",
  config,
  profile: "runtime"
});
// → Error: Missing "text"

// ⏱  58 seconds fixing...

// Iteration 3
config.text = "Hello!";

const result3 = validate_node({
  nodeType: "nodes-base.slack",
  config,
  profile: "runtime"
});
// → Valid! ✅
```

**This is normal!** Don't be discouraged by multiple iterations.

---

## Validation Profiles

Choose the right profile for your stage:

### minimal
**Use when**: Quick checks during editing

**Validates**:
- Only required fields
- Basic structure

**Pros**: Fastest, most permissive
**Cons**: May miss issues

### runtime (RECOMMENDED)
**Use when**: Pre-deployment validation

**Validates**:
- Required fields
- Value types
- Allowed values
- Basic dependencies

**Pros**: Balanced, catches real errors
**Cons**: Some edge cases missed

**This is the recommended profile for most use cases**

### ai-friendly
**Use when**: AI-generated configurations

**Validates**:
- Same as runtime
- Reduces false positives
- More tolerant of minor issues

**Pros**: Less noisy for AI workflows
**Cons**: May allow some questionable configs

### strict
**Use when**: Production deployment, critical workflows

**Validates**:
- Everything
- Best practices
- Performance concerns
- Security issues

**Pros**: Maximum safety
**Cons**: Many warnings, some false positives

---

## Common Error Types

### 1. missing_required
**What it means**: A required field is not provided

**How to fix**:
1. Use `get_node` to see required fields
2. Add the missing field to your configuration
3. Provide an appropriate value

**Example**:
```javascript
// Error
{
  "type": "missing_required",
  "property": "channel",
  "message": "Channel name is required"
}

// Fix
config.channel = "#general";
```

### 2. invalid_value
**What it means**: Value doesn't match allowed options

**How to fix**:
1. Check error message for allowed values
2. Use `get_node` to see options
3. Update to a valid value

**Example**:
```javascript
// Error
{
  "type": "invalid_value",
  "property": "operation",
  "message": "Operation must be one of: post, update, delete",
  "current": "send"
}

// Fix
config.operation = "post";  // Use valid operation
```

### 3. type_mismatch
**What it means**: Wrong data type for field

**How to fix**:
1. Check expected type in error message
2. Convert value to correct type

**Example**:
```javascript
// Error
{
  "type": "type_mismatch",
  "property": "limit",
  "message": "Expected number, got string",
  "current": "100"
}

// Fix
config.limit = 100;  // Number, not string
```

### 4. invalid_expression
**What it means**: Expression syntax error

**How to fix**:
1. Use n8n Expression Syntax skill
2. Check for missing `{{}}` or typos
3. Verify node/field references

**Example**:
```javascript
// Error
{
  "type": "invalid_expression",
  "property": "text",
  "message": "Invalid expression: $json.name",
  "current": "$json.name"
}

// Fix
config.text = "={{$json.name}}";  // Add {{}}
```

### 5. invalid_reference
**What it means**: Referenced node doesn't exist

**How to fix**:
1. Check node name spelling
2. Verify node exists in workflow
3. Update reference to correct name

**Example**:
```javascript
// Error
{
  "type": "invalid_reference",
  "property": "expression",
  "message": "Node 'HTTP Requets' does not exist",
  "current": "={{$node['HTTP Requets'].json.data}}"
}

// Fix - correct typo
config.expression = "={{$node['HTTP Request'].json.data}}";
```

---

## Auto-Sanitization System

### What It Does
**Automatically fixes common operator structure issues** on ANY workflow update

**Runs when**:
- `n8n_create_workflow`
- `n8n_update_partial_workflow`
- Any workflow save operation

### What It Fixes

#### 1. Binary Operators (Two Values)
**Operators**: equals, notEquals, contains, notContains, greaterThan, lessThan, startsWith, endsWith

**Fix**: Removes `singleValue` property (binary operators compare two values)

**Before**:
```javascript
{
  "type": "boolean",
  "operation": "equals",
  "singleValue": true  // ❌ Wrong!
}
```

**After** (automatic):
```javascript
{
  "type": "boolean",
  "operation": "equals"
  // singleValue removed ✅
}
```

#### 2. Unary Operators (One Value)
**Operators**: isEmpty, isNotEmpty, true, false

**Fix**: Adds `singleValue: true` (unary operators check single value)

**Before**:
```javascript
{
  "type": "boolean",
  "operation": "isEmpty"
  // Missing singleValue ❌
}
```

**After** (automatic):
```javascript
{
  "type": "boolean",
  "operation": "isEmpty",
  "singleValue": true  // ✅ Added
}
```

#### 3. IF/Switch Metadata
**Fix**: Adds complete `conditions.options` metadata for IF v2.2+ and Switch v3.2+

### What It CANNOT Fix

#### 1. Broken Connections
References to non-existent nodes

**Solution**: Use `cleanStaleConnections` operation in `n8n_update_partial_workflow`

#### 2. Branch Count Mismatches
3 Switch rules but only 2 output connections

**Solution**: Add missing connections or remove extra rules

#### 3. Paradoxical Corrupt States
API returns corrupt data but rejects updates

**Solution**: May require manual database intervention

---

## False Positives

### What Are They?
Validation warnings that are technically "wrong" but acceptable in your use case

### Common False Positives

#### 1. "Missing error handling"
**Warning**: No error handling configured

**When acceptable**:
- Simple workflows where failures are obvious
- Testing/development workflows
- Non-critical notifications

**When to fix**: Production workflows handling important data

#### 2. "No retry logic"
**Warning**: Node doesn't retry on failure

**When acceptable**:
- APIs with their own retry logic
- Idempotent operations
- Manual trigger workflows

**When to fix**: Flaky external services, production automation

#### 3. "Missing rate limiting"
**Warning**: No rate limiting for API calls

**When acceptable**:
- Internal APIs with no limits
- Low-volume workflows
- APIs with server-side rate limiting

**When to fix**: Public APIs, high-volume workflows

#### 4. "Unbounded query"
**Warning**: SELECT without LIMIT

**When acceptable**:
- Small known datasets
- Aggregation queries
- Development/testing

**When to fix**: Production queries on large tables

### Reducing False Positives

**Use `ai-friendly` profile**:
```javascript
validate_node({
  nodeType: "nodes-base.slack",
  config: {...},
  profile: "ai-friendly"  // Fewer false positives
})
```

---

## Validation Result Structure

### Complete Response
```javascript
{
  "valid": false,
  "errors": [
    {
      "type": "missing_required",
      "property": "channel",
      "message": "Channel name is required",
      "fix": "Provide a channel name (lowercase, no spaces)"
    }
  ],
  "warnings": [
    {
      "type": "best_practice",
      "property": "errorHandling",
      "message": "Slack API can have rate limits",
      "suggestion": "Add onError: 'continueRegularOutput'"
    }
  ],
  "suggestions": [
    {
      "type": "optimization",
      "message": "Consider using batch operations for multiple messages"
    }
  ],
  "summary": {
    "hasErrors": true,
    "errorCount": 1,
    "warningCount": 1,
    "suggestionCount": 1
  }
}
```

### How to Read It

#### 1. Check `valid` field
```javascript
if (result.valid) {
  // ✅ Configuration is valid
} else {
  // ❌ Has errors - must fix before deployment
}
```

#### 2. Fix errors first
```javascript
result.errors.forEach(error => {
  console.log(`Error in ${error.property}: ${error.message}`);
  console.log(`Fix: ${error.fix}`);
});
```

#### 3. Review warnings
```javascript
result.warnings.forEach(warning => {
  console.log(`Warning: ${warning.message}`);
  console.log(`Suggestion: ${warning.suggestion}`);
  // Decide if you need to address this
});
```

#### 4. Consider suggestions
```javascript
// Optional improvements
// Not required but may enhance workflow
```

---

## Workflow Validation

### validate_workflow (Structure)
**Validates entire workflow**, not just individual nodes

**Checks**:
1. **Node configurations** - Each node valid
2. **Connections** - No broken references
3. **Expressions** - Syntax and references valid
4. **Flow** - Logical workflow structure

**Example**:
```javascript
validate_workflow({
  workflow: {
    nodes: [...],
    connections: {...}
  },
  options: {
    validateNodes: true,
    validateConnections: true,
    validateExpressions: true,
    profile: "runtime"
  }
})
```

### Common Workflow Errors

#### 1. Broken Connections
```json
{
  "error": "Connection from 'Transform' to 'NonExistent' - target node not found"
}
```

**Fix**: Remove stale connection or create missing node

#### 2. Circular Dependencies
```json
{
  "error": "Circular dependency detected: Node A → Node B → Node A"
}
```

**Fix**: Restructure workflow to remove loop

#### 3. Multiple Start Nodes
```json
{
  "warning": "Multiple trigger nodes found - only one will execute"
}
```

**Fix**: Remove extra triggers or split into separate workflows

#### 4. Disconnected Nodes
```json
{
  "warning": "Node 'Transform' is not connected to workflow flow"
}
```

**Fix**: Connect node or remove if unused

---

## Recovery Strategies

### Strategy 1: Start Fresh
**When**: Configuration is severely broken

**Steps**:
1. Note required fields from `get_node`
2. Create minimal valid configuration
3. Add features incrementally
4. Validate after each addition

### Strategy 2: Binary Search
**When**: Workflow validates but executes incorrectly

**Steps**:
1. Remove half the nodes
2. Validate and test
3. If works: problem is in removed nodes
4. If fails: problem is in remaining nodes
5. Repeat until problem isolated

### Strategy 3: Clean Stale Connections
**When**: "Node not found" errors

**Steps**:
```javascript
n8n_update_partial_workflow({
  id: "workflow-id",
  operations: [{
    type: "cleanStaleConnections"
  }]
})
```

### Strategy 4: Use Auto-fix
**When**: Operator structure errors

**Steps**:
```javascript
n8n_autofix_workflow({
  id: "workflow-id",
  applyFixes: false  // Preview first
})

// Review fixes, then apply
n8n_autofix_workflow({
  id: "workflow-id",
  applyFixes: true
})
```

---

## Best Practices

### ✅ Do

- Validate after every significant change
- Read error messages completely
- Fix errors iteratively (one at a time)
- Use `runtime` profile for pre-deployment
- Check `valid` field before assuming success
- Trust auto-sanitization for operator issues
- Use `get_node` when unclear about requirements
- Document false positives you accept

### ❌ Don't

- Skip validation before activation
- Try to fix all errors at once
- Ignore error messages
- Use `strict` profile during development (too noisy)
- Assume validation passed (always check result)
- Manually fix auto-sanitization issues
- Deploy with unresolved errors
- Ignore all warnings (some are important!)

---

## Detailed Guides

For comprehensive error catalogs and false positive examples:

- **ERROR_CATALOG.md** - Complete list of error types with examples
- **FALSE_POSITIVES.md** - When warnings are acceptable

---

## Summary

**Key Points**:
1. **Validation is iterative** (avg 2-3 cycles, 23s + 58s)
2. **Errors must be fixed**, warnings are optional
3. **Auto-sanitization** fixes operator structures automatically
4. **Use runtime profile** for balanced validation
5. **False positives exist** - learn to recognize them
6. **Read error messages** - they contain fix guidance

**Validation Process**:
1. Validate → Read errors → Fix → Validate again
2. Repeat until valid (usually 2-3 iterations)
3. Review warnings and decide if acceptable
4. Deploy with confidence

**Related Skills**:
- n8n MCP Tools Expert - Use validation tools correctly
- n8n Expression Syntax - Fix expression errors
- n8n Node Configuration - Understand required fields

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: n8n-workflow-patterns
description: "Proven architectural patterns for building n8n workflows."
risk: critical
source: community
---

# n8n Workflow Patterns

Proven architectural patterns for building n8n workflows.

## When to Use
- You need to choose an architectural pattern for an n8n workflow before building it.
- The task involves webhook processing, API integration, scheduled jobs, database sync, or AI-agent workflow design.
- You want a high-level workflow structure rather than node-by-node troubleshooting.

---

## The 5 Core Patterns

Based on analysis of real workflow usage:

1. **Webhook Processing** (Most Common)
   - Receive HTTP requests → Process → Output
   - Pattern: Webhook → Validate → Transform → Respond/Notify

2. **[HTTP API Integration]**
   - Fetch from REST APIs → Transform → Store/Use
   - Pattern: Trigger → HTTP Request → Transform → Action → Error Handler

3. **Database Operations**
   - Read/Write/Sync database data
   - Pattern: Schedule → Query → Transform → Write → Verify

4. **AI Agent Workflow**
   - AI agents with tools and memory
   - Pattern: Trigger → AI Agent (Model + Tools + Memory) → Output

5. **Scheduled Tasks**
   - Recurring automation workflows
   - Pattern: Schedule → Fetch → Process → Deliver → Log

---

## Pattern Selection Guide

### When to use each pattern:

**Webhook Processing** - Use when:
- Receiving data from external systems
- Building integrations (Slack commands, form submissions, GitHub webhooks)
- Need instant response to events
- Example: "Receive Stripe payment webhook → Update database → Send confirmation"

**HTTP API Integration** - Use when:
- Fetching data from external APIs
- Synchronizing with third-party services
- Building data pipelines
- Example: "Fetch GitHub issues → Transform → Create Jira tickets"

**Database Operations** - Use when:
- Syncing between databases
- Running database queries on schedule
- ETL workflows
- Example: "Read Postgres records → Transform → Write to MySQL"

**AI Agent Workflow** - Use when:
- Building conversational AI
- Need AI with tool access
- Multi-step reasoning tasks
- Example: "Chat with AI that can search docs, query database, send emails"

**Scheduled Tasks** - Use when:
- Recurring reports or summaries
- Periodic data fetching
- Maintenance tasks
- Example: "Daily: Fetch analytics → Generate report → Email team"

---

## Common Workflow Components

All patterns share these building blocks:

### 1. Triggers
- **Webhook** - HTTP endpoint (instant)
- **Schedule** - Cron-based timing (periodic)
- **Manual** - Click to execute (testing)
- **Polling** - Check for changes (intervals)

### 2. Data Sources
- **HTTP Request** - REST APIs
- **Database nodes** - Postgres, MySQL, MongoDB
- **Service nodes** - Slack, Google Sheets, etc.
- **Code** - Custom JavaScript/Python

### 3. Transformation
- **Set** - Map/transform fields
- **Code** - Complex logic
- **IF/Switch** - Conditional routing
- **Merge** - Combine data streams

### 4. Outputs
- **HTTP Request** - Call APIs
- **Database** - Write data
- **Communication** - Email, Slack, Discord
- **Storage** - Files, cloud storage

### 5. Error Handling
- **Error Trigger** - Catch workflow errors
- **IF** - Check for error conditions
- **Stop and Error** - Explicit failure
- **Continue On Fail** - Per-node setting

---

## Workflow Creation Checklist

When building ANY workflow, follow this checklist:

### Planning Phase
- [ ] Identify the pattern (webhook, API, database, AI, scheduled)
- [ ] List required nodes (use search_nodes)
- [ ] Understand data flow (input → transform → output)
- [ ] Plan error handling strategy

### Implementation Phase
- [ ] Create workflow with appropriate trigger
- [ ] Add data source nodes
- [ ] Configure authentication/credentials
- [ ] Add transformation nodes (Set, Code, IF)
- [ ] Add output/action nodes
- [ ] Configure error handling

### Validation Phase
- [ ] Validate each node configuration (validate_node)
- [ ] Validate complete workflow (validate_workflow)
- [ ] Test with sample data
- [ ] Handle edge cases (empty data, errors)

### Deployment Phase
- [ ] Review workflow settings (execution order, timeout, error handling)
- [ ] Activate workflow using `activateWorkflow` operation
- [ ] Monitor first executions
- [ ] Document workflow purpose and data flow

---

## Data Flow Patterns

### Linear Flow
```
Trigger → Transform → Action → End
```
**Use when**: Simple workflows with single path

### Branching Flow
```
Trigger → IF → [True Path]
             └→ [False Path]
```
**Use when**: Different actions based on conditions

### Parallel Processing
```
Trigger → [Branch 1] → Merge
       └→ [Branch 2] ↗
```
**Use when**: Independent operations that can run simultaneously

### Loop Pattern
```
Trigger → Split in Batches → Process → Loop (until done)
```
**Use when**: Processing large datasets in chunks

### Error Handler Pattern
```
Main Flow → [Success Path]
         └→ [Error Trigger → Error Handler]
```
**Use when**: Need separate error handling workflow

---

## Common Gotchas

### 1. Webhook Data Structure
**Problem**: Can't access webhook payload data

**Solution**: Data is nested under `$json.body`
```javascript
❌ {{$json.email}}
✅ {{$json.body.email}}
```
See: n8n Expression Syntax skill

### 2. Multiple Input Items
**Problem**: Node processes all input items, but I only want one

**Solution**: Use "Execute Once" mode or process first item only
```javascript
{{$json[0].field}}  // First item only
```

### 3. Authentication Issues
**Problem**: API calls failing with 401/403

**Solution**:
- Configure credentials properly
- Use the "Credentials" section, not parameters
- Test credentials before workflow activation

### 4. Node Execution Order
**Problem**: Nodes executing in unexpected order

**Solution**: Check workflow settings → Execution Order
- v0: Top-to-bottom (legacy)
- v1: Connection-based (recommended)

### 5. Expression Errors
**Problem**: Expressions showing as literal text

**Solution**: Use {{}} around expressions
- See n8n Expression Syntax skill for details

---

## Integration with Other Skills

These skills work together with Workflow Patterns:

**n8n MCP Tools Expert** - Use to:
- Find nodes for your pattern (search_nodes)
- Understand node operations (get_node)
- Create workflows (n8n_create_workflow)
- Deploy templates (n8n_deploy_template)
- Use ai_agents_guide for AI pattern guidance

**n8n Expression Syntax** - Use to:
- Write expressions in transformation nodes
- Access webhook data correctly ({{$json.body.field}})
- Reference previous nodes ({{$node["Node Name"].json.field}})

**n8n Node Configuration** - Use to:
- Configure specific operations for pattern nodes
- Understand node-specific requirements

**n8n Validation Expert** - Use to:
- Validate workflow structure
- Fix validation errors
- Ensure workflow correctness before deployment

---

## Pattern Statistics

Common workflow patterns:

**Most Common Triggers**:
1. Webhook - 35%
2. Schedule (periodic tasks) - 28%
3. Manual (testing/admin) - 22%
4. Service triggers (Slack, email, etc.) - 15%

**Most Common Transformations**:
1. Set (field mapping) - 68%
2. Code (custom logic) - 42%
3. IF (conditional routing) - 38%
4. Switch (multi-condition) - 18%

**Most Common Outputs**:
1. HTTP Request (APIs) - 45%
2. Slack - 32%
3. Database writes - 28%
4. Email - 24%

**Average Workflow Complexity**:
- Simple (3-5 nodes): 42%
- Medium (6-10 nodes): 38%
- Complex (11+ nodes): 20%

---

## Quick Start Examples

### Example 1: Simple Webhook → Slack
```
1. Webhook (path: "form-submit", POST)
2. Set (map form fields)
3. Slack (post message to #notifications)
```

### Example 2: Scheduled Report
```
1. Schedule (daily at 9 AM)
2. HTTP Request (fetch analytics)
3. Code (aggregate data)
4. Email (send formatted report)
5. Error Trigger → Slack (notify on failure)
```

### Example 3: Database Sync
```
1. Schedule (every 15 minutes)
2. Postgres (query new records)
3. IF (check if records exist)
4. MySQL (insert records)
5. Postgres (update sync timestamp)
```

### Example 4: AI Assistant
```
1. Webhook (receive chat message)
2. AI Agent
   ├─ OpenAI Chat Model (ai_languageModel)
   ├─ HTTP Request Tool (ai_tool)
   ├─ Database Tool (ai_tool)
   └─ Window Buffer Memory (ai_memory)
3. Webhook Response (send AI reply)
```

### Example 5: API Integration
```
1. Manual Trigger (for testing)
2. HTTP Request (GET /api/users)
3. Split In Batches (process 100 at a time)
4. Set (transform user data)
5. Postgres (upsert users)
6. Loop (back to step 3 until done)
```

---

## Detailed Pattern Files

For comprehensive guidance on each pattern:

- **webhook_processing.md** - Webhook patterns, data structure, response handling
- **http_api_integration** - REST APIs, authentication, pagination, retries
- **database_operations.md** - Queries, sync, transactions, batch processing
- **ai_agent_workflow.md** - AI agents, tools, memory, langchain nodes
- **scheduled_tasks.md** - Cron schedules, reports, maintenance tasks

---

## Real Template Examples

From n8n template library:

**Template #2947**: Weather to Slack
- Pattern: Scheduled Task
- Nodes: Schedule → HTTP Request (weather API) → Set → Slack
- Complexity: Simple (4 nodes)

**Webhook Processing**: Most common pattern
- Most common: Form submissions, payment webhooks, chat integrations

**HTTP API**: Common pattern
- Most common: Data fetching, third-party integrations

**Database Operations**: Common pattern
- Most common: ETL, data sync, backup workflows

**AI Agents**: Growing in usage
- Most common: Chatbots, content generation, data analysis

Use `search_templates` and `get_template` from n8n-mcp tools to find examples!

---

## Best Practices

### ✅ Do

- Start with the simplest pattern that solves your problem
- Plan your workflow structure before building
- Use error handling on all workflows
- Test with sample data before activation
- Follow the workflow creation checklist
- Use descriptive node names
- Document complex workflows (notes field)
- Monitor workflow executions after deployment

### ❌ Don't

- Build workflows in one shot (iterate! avg 56s between edits)
- Skip validation before activation
- Ignore error scenarios
- Use complex patterns when simple ones suffice
- Hardcode credentials in parameters
- Forget to handle empty data cases
- Mix multiple patterns without clear boundaries
- Deploy without testing

---

## Summary

**Key Points**:
1. **5 core patterns** cover 90%+ of workflow use cases
2. **Webhook processing** is the most common pattern
3. Use the **workflow creation checklist** for every workflow
4. **Plan pattern** → **Select nodes** → **Build** → **Validate** → **Deploy**
5. Integrate with other skills for complete workflow development

**Next Steps**:
1. Identify your use case pattern
2. Read the detailed pattern file
3. Use n8n MCP Tools Expert to find nodes
4. Follow the workflow creation checklist
5. Use n8n Validation Expert to validate

**Related Skills**:
- n8n MCP Tools Expert - Find and configure nodes
- n8n Expression Syntax - Write expressions correctly
- n8n Validation Expert - Validate and fix errors
- n8n Node Configuration - Configure specific operations

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.


---
name: nanobanana-ppt-skills
description: "AI-powered PPT generation with document analysis and styled images"
risk: safe
source: "https://github.com/op7418/NanoBanana-PPT-Skills"
date_added: "2026-02-27"
---

# Nanobanana Ppt Skills

## Overview

AI-powered PPT generation with document analysis and styled images

## When to Use This Skill

Use this skill when you need to work with ai-powered ppt generation with document analysis and styled images.

## Instructions

This skill provides guidance and patterns for ai-powered ppt generation with document analysis and styled images.

For more information, see the [source repository](https://github.com/op7418/NanoBanana-PPT-Skills).

## Limitations
- Use this skill only when the task clearly matches the scope described above.
- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.