Files
n8n-skills/skills/n8n-node-configuration/OPERATION_PATTERNS.md
czlonkowski 33e83c29dc refactor: Remove research context from skill content
Cleaned up all skills to remove research/telemetry context that was used
during design but is not needed at runtime when AI agents use the skills.

## Changes Made

### Pattern 1: Research Framing Removed
- "From analysis of X workflows/events" → Removed
- "From telemetry analysis:" → Replaced with operational context
- "Based on X real workflows" → Simplified to general statements

### Pattern 2: Popularity Metrics Removed
- "**Popularity**: Second most common (892 templates)" → Removed entirely
- "813 searches", "456 templates", etc. → Removed

### Pattern 3: Frequency Percentages Converted
- "**Frequency**: 45% of errors" → "Most common error"
- "**Frequency**: 28%" → "Second most common"
- "**Frequency**: 12%" → "Common error"
- Percentages in tables → Priority levels (Highest/High/Medium/Low)

### Pattern 4: Operational Guidance Kept
-  Success rates (91.7%) - helps tool selection
-  Average times (18s, 56s) - sets expectations
-  Relative priority (most common, typical) - guides decisions
-  Iteration counts (2-3 cycles) - manages expectations

## Files Modified (19 files across 4 skills)

**Skill #2: MCP Tools Expert (5 files)**
- Removed telemetry occurrence counts
- Kept success rates and average times

**Skill #3: Workflow Patterns (7 files)**
- Removed all popularity metrics from pattern files
- Removed "From analysis of 31,917 workflows"
- Removed template counts

**Skill #4: Validation Expert (4 files)**
- Converted frequency % to priority levels
- Removed "From analysis of 19,113 errors"
- Removed telemetry loop counts (kept iteration guidance)

**Skill #5: Node Configuration (3 files)**
- Removed workflow update counts
- Removed essentials call counts
- Kept success rates and timing guidance

## Result

Skills now provide clean, focused runtime guidance without research
justification. Content is more actionable for AI agents using the skills.

All technical guidance, examples, patterns, and operational metrics preserved.
Only removed: research methodology, data source attribution, and statistical
justification for design decisions.

🤖 Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
2025-10-20 11:45:04 +02:00

15 KiB

Operation Patterns Guide

Common node configuration patterns organized by node type and operation.


Overview

Purpose: Quick reference for common node configurations

Coverage: Top 20 most-used nodes from 525 available

Pattern format:

  • Minimal valid configuration
  • Common options
  • Real-world examples
  • Gotchas and tips

HTTP & API Nodes

HTTP Request (nodes-base.httpRequest)

Most versatile node for HTTP operations

GET Request

Minimal:

{
  "method": "GET",
  "url": "https://api.example.com/users",
  "authentication": "none"
}

With query parameters:

{
  "method": "GET",
  "url": "https://api.example.com/users",
  "authentication": "none",
  "sendQuery": true,
  "queryParameters": {
    "parameters": [
      {
        "name": "limit",
        "value": "100"
      },
      {
        "name": "offset",
        "value": "={{$json.offset}}"
      }
    ]
  }
}

With authentication:

{
  "method": "GET",
  "url": "https://api.example.com/users",
  "authentication": "predefinedCredentialType",
  "nodeCredentialType": "httpHeaderAuth"
}

POST with JSON

Minimal:

{
  "method": "POST",
  "url": "https://api.example.com/users",
  "authentication": "none",
  "sendBody": true,
  "body": {
    "contentType": "json",
    "content": {
      "name": "John Doe",
      "email": "john@example.com"
    }
  }
}

With expressions:

{
  "method": "POST",
  "url": "https://api.example.com/users",
  "authentication": "none",
  "sendBody": true,
  "body": {
    "contentType": "json",
    "content": {
      "name": "={{$json.name}}",
      "email": "={{$json.email}}",
      "metadata": {
        "source": "n8n",
        "timestamp": "={{$now.toISO()}}"
      }
    }
  }
}

Gotcha: Remember sendBody: true for POST/PUT/PATCH!

PUT/PATCH Request

Pattern: Same as POST, but method changes

{
  "method": "PUT",  // or "PATCH"
  "url": "https://api.example.com/users/123",
  "authentication": "none",
  "sendBody": true,
  "body": {
    "contentType": "json",
    "content": {
      "name": "Updated Name"
    }
  }
}

DELETE Request

Minimal (no body):

{
  "method": "DELETE",
  "url": "https://api.example.com/users/123",
  "authentication": "none"
}

With body (some APIs allow):

{
  "method": "DELETE",
  "url": "https://api.example.com/users",
  "authentication": "none",
  "sendBody": true,
  "body": {
    "contentType": "json",
    "content": {
      "ids": ["123", "456"]
    }
  }
}

Webhook (nodes-base.webhook)

Most common trigger - 813 searches!

Basic Webhook

Minimal:

{
  "path": "my-webhook",
  "httpMethod": "POST",
  "responseMode": "onReceived"
}

Gotcha: Webhook data is under $json.body, not $json!

// ❌ Wrong
{
  "text": "={{$json.email}}"
}

// ✅ Correct
{
  "text": "={{$json.body.email}}"
}

Webhook with Authentication

Header auth:

{
  "path": "secure-webhook",
  "httpMethod": "POST",
  "responseMode": "onReceived",
  "authentication": "headerAuth",
  "options": {
    "responseCode": 200,
    "responseData": "{\n  \"success\": true\n}"
  }
}

Webhook Returning Data

Custom response:

{
  "path": "my-webhook",
  "httpMethod": "POST",
  "responseMode": "lastNode",  // Return data from last node
  "options": {
    "responseCode": 201,
    "responseHeaders": {
      "entries": [
        {
          "name": "Content-Type",
          "value": "application/json"
        }
      ]
    }
  }
}

Communication Nodes

Slack (nodes-base.slack)

Popular choice for AI agent workflows

Post Message

Minimal:

{
  "resource": "message",
  "operation": "post",
  "channel": "#general",
  "text": "Hello from n8n!"
}

With dynamic content:

{
  "resource": "message",
  "operation": "post",
  "channel": "={{$json.channel}}",
  "text": "New user: {{$json.name}} ({{$json.email}})"
}

With attachments:

{
  "resource": "message",
  "operation": "post",
  "channel": "#alerts",
  "text": "Error Alert",
  "attachments": [
    {
      "color": "#ff0000",
      "fields": [
        {
          "title": "Error Type",
          "value": "={{$json.errorType}}"
        },
        {
          "title": "Timestamp",
          "value": "={{$now.toLocaleString()}}"
        }
      ]
    }
  ]
}

Gotcha: Channel must start with # for public channels or be a channel ID!

Update Message

Minimal:

{
  "resource": "message",
  "operation": "update",
  "messageId": "1234567890.123456",  // From previous message post
  "text": "Updated message content"
}

Note: messageId required, channel optional (can be inferred)

Create Channel

Minimal:

{
  "resource": "channel",
  "operation": "create",
  "name": "new-project-channel",  // Lowercase, no spaces
  "isPrivate": false
}

Gotcha: Channel name must be lowercase, no spaces, 1-80 chars!


Gmail (nodes-base.gmail)

Popular for email automation

Send Email

Minimal:

{
  "resource": "message",
  "operation": "send",
  "to": "user@example.com",
  "subject": "Hello from n8n",
  "message": "This is the email body"
}

With dynamic content:

{
  "resource": "message",
  "operation": "send",
  "to": "={{$json.email}}",
  "subject": "Order Confirmation #{{$json.orderId}}",
  "message": "Dear {{$json.name}},\n\nYour order has been confirmed.\n\nThank you!",
  "options": {
    "ccList": "admin@example.com",
    "replyTo": "support@example.com"
  }
}

Get Email

Minimal:

{
  "resource": "message",
  "operation": "getAll",
  "returnAll": false,
  "limit": 10
}

With filters:

{
  "resource": "message",
  "operation": "getAll",
  "returnAll": false,
  "limit": 50,
  "filters": {
    "q": "is:unread from:important@example.com",
    "labelIds": ["INBOX"]
  }
}

Database Nodes

Postgres (nodes-base.postgres)

Database operations - 456 templates

Execute Query

Minimal (SELECT):

{
  "operation": "executeQuery",
  "query": "SELECT * FROM users WHERE active = true LIMIT 100"
}

With parameters (SQL injection prevention):

{
  "operation": "executeQuery",
  "query": "SELECT * FROM users WHERE email = $1 AND active = $2",
  "additionalFields": {
    "mode": "list",
    "queryParameters": "user@example.com,true"
  }
}

Gotcha: ALWAYS use parameterized queries for user input!

// ❌ BAD - SQL injection risk!
{
  "query": "SELECT * FROM users WHERE email = '{{$json.email}}'"
}

// ✅ GOOD - Parameterized
{
  "query": "SELECT * FROM users WHERE email = $1",
  "additionalFields": {
    "mode": "list",
    "queryParameters": "={{$json.email}}"
  }
}

Insert

Minimal:

{
  "operation": "insert",
  "table": "users",
  "columns": "name,email,created_at",
  "additionalFields": {
    "mode": "list",
    "queryParameters": "John Doe,john@example.com,NOW()"
  }
}

With expressions:

{
  "operation": "insert",
  "table": "users",
  "columns": "name,email,metadata",
  "additionalFields": {
    "mode": "list",
    "queryParameters": "={{$json.name}},={{$json.email}},{{JSON.stringify($json)}}"
  }
}

Update

Minimal:

{
  "operation": "update",
  "table": "users",
  "updateKey": "id",
  "columns": "name,email",
  "additionalFields": {
    "mode": "list",
    "queryParameters": "={{$json.id}},Updated Name,newemail@example.com"
  }
}

Data Transformation Nodes

Set (nodes-base.set)

Most used transformation - 68% of workflows!

Set Fixed Values

Minimal:

{
  "mode": "manual",
  "duplicateItem": false,
  "assignments": {
    "assignments": [
      {
        "name": "status",
        "value": "active",
        "type": "string"
      },
      {
        "name": "count",
        "value": 100,
        "type": "number"
      }
    ]
  }
}

Set from Input Data

Mapping data:

{
  "mode": "manual",
  "duplicateItem": false,
  "assignments": {
    "assignments": [
      {
        "name": "fullName",
        "value": "={{$json.firstName}} {{$json.lastName}}",
        "type": "string"
      },
      {
        "name": "email",
        "value": "={{$json.email.toLowerCase()}}",
        "type": "string"
      },
      {
        "name": "timestamp",
        "value": "={{$now.toISO()}}",
        "type": "string"
      }
    ]
  }
}

Gotcha: Use correct type for each field!

// ❌ Wrong type
{
  "name": "age",
  "value": "25",      // String
  "type": "string"    // Will be string "25"
}

// ✅ Correct type
{
  "name": "age",
  "value": 25,        // Number
  "type": "number"    // Will be number 25
}

Code (nodes-base.code)

JavaScript execution - 42% of workflows

Simple Transformation

Minimal:

{
  "mode": "runOnceForAllItems",
  "jsCode": "return $input.all().map(item => ({\n  json: {\n    name: item.json.name.toUpperCase(),\n    email: item.json.email\n  }\n}));"
}

Per-item processing:

{
  "mode": "runOnceForEachItem",
  "jsCode": "// Process each item\nconst data = $input.item.json;\n\nreturn {\n  json: {\n    fullName: `${data.firstName} ${data.lastName}`,\n    email: data.email.toLowerCase(),\n    timestamp: new Date().toISOString()\n  }\n};"
}

Gotcha: In Code nodes, use $input.item.json or $input.all(), NOT {{...}}!

// ❌ Wrong - expressions don't work in Code nodes
{
  "jsCode": "const name = '={{$json.name}}';"
}

// ✅ Correct - direct access
{
  "jsCode": "const name = $input.item.json.name;"
}

Conditional Nodes

IF (nodes-base.if)

Conditional logic - 38% of workflows

String Comparison

Equals (binary):

{
  "conditions": {
    "string": [
      {
        "value1": "={{$json.status}}",
        "operation": "equals",
        "value2": "active"
      }
    ]
  }
}

Contains (binary):

{
  "conditions": {
    "string": [
      {
        "value1": "={{$json.email}}",
        "operation": "contains",
        "value2": "@example.com"
      }
    ]
  }
}

isEmpty (unary):

{
  "conditions": {
    "string": [
      {
        "value1": "={{$json.email}}",
        "operation": "isEmpty"
        // No value2 - unary operator
        // singleValue: true added by auto-sanitization
      }
    ]
  }
}

Gotcha: Unary operators (isEmpty, isNotEmpty) don't need value2!

Number Comparison

Greater than:

{
  "conditions": {
    "number": [
      {
        "value1": "={{$json.age}}",
        "operation": "larger",
        "value2": 18
      }
    ]
  }
}

Boolean Comparison

Is true:

{
  "conditions": {
    "boolean": [
      {
        "value1": "={{$json.isActive}}",
        "operation": "true"
        // Unary - no value2
      }
    ]
  }
}

Multiple Conditions (AND)

All must match:

{
  "conditions": {
    "string": [
      {
        "value1": "={{$json.status}}",
        "operation": "equals",
        "value2": "active"
      }
    ],
    "number": [
      {
        "value1": "={{$json.age}}",
        "operation": "larger",
        "value2": 18
      }
    ]
  },
  "combineOperation": "all"  // AND logic
}

Multiple Conditions (OR)

Any can match:

{
  "conditions": {
    "string": [
      {
        "value1": "={{$json.status}}",
        "operation": "equals",
        "value2": "active"
      },
      {
        "value1": "={{$json.status}}",
        "operation": "equals",
        "value2": "pending"
      }
    ]
  },
  "combineOperation": "any"  // OR logic
}

Switch (nodes-base.switch)

Multi-way routing - 18% of workflows

Basic Switch

Minimal:

{
  "mode": "rules",
  "rules": {
    "rules": [
      {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.status}}",
              "operation": "equals",
              "value2": "active"
            }
          ]
        }
      },
      {
        "conditions": {
          "string": [
            {
              "value1": "={{$json.status}}",
              "operation": "equals",
              "value2": "pending"
            }
          ]
        }
      }
    ]
  },
  "fallbackOutput": "extra"  // Catch-all for non-matching
}

Gotcha: Number of rules must match number of outputs!


AI Nodes

OpenAI (nodes-langchain.openAi)

AI operations - 234 templates

Chat Completion

Minimal:

{
  "resource": "chat",
  "operation": "complete",
  "messages": {
    "values": [
      {
        "role": "user",
        "content": "={{$json.prompt}}"
      }
    ]
  }
}

With system prompt:

{
  "resource": "chat",
  "operation": "complete",
  "messages": {
    "values": [
      {
        "role": "system",
        "content": "You are a helpful assistant specialized in customer support."
      },
      {
        "role": "user",
        "content": "={{$json.userMessage}}"
      }
    ]
  },
  "options": {
    "temperature": 0.7,
    "maxTokens": 500
  }
}

Schedule Nodes

Schedule Trigger (nodes-base.scheduleTrigger)

Time-based workflows - 28% have schedule triggers

Daily at Specific Time

Minimal:

{
  "rule": {
    "interval": [
      {
        "field": "hours",
        "hoursInterval": 24
      }
    ],
    "hour": 9,
    "minute": 0,
    "timezone": "America/New_York"
  }
}

Gotcha: Always set timezone explicitly!

// ❌ Bad - uses server timezone
{
  "rule": {
    "interval": [...]
  }
}

// ✅ Good - explicit timezone
{
  "rule": {
    "interval": [...],
    "timezone": "America/New_York"
  }
}

Every N Minutes

Minimal:

{
  "rule": {
    "interval": [
      {
        "field": "minutes",
        "minutesInterval": 15
      }
    ]
  }
}

Cron Expression

Advanced scheduling:

{
  "mode": "cron",
  "cronExpression": "0 */2 * * *",  // Every 2 hours
  "timezone": "America/New_York"
}

Summary

Key Patterns by Category:

Category Most Common Key Gotcha
HTTP/API GET, POST JSON Remember sendBody: true
Webhooks POST receiver Data under $json.body
Communication Slack post Channel format (#name)
Database SELECT with params Use parameterized queries
Transform Set assignments Correct type per field
Conditional IF string equals Unary vs binary operators
AI OpenAI chat System + user messages
Schedule Daily at time Set timezone explicitly

Configuration Approach:

  1. Use patterns as starting point
  2. Adapt to your use case
  3. Validate configuration
  4. Iterate based on errors
  5. Deploy when valid

Related Files: