n8n Workflow Engineering

# n8n Workflow Mastery — Complete Automation Engineering System

Safety Notice

This listing is from the official public ClawHub registry. Review SKILL.md and referenced scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "n8n Workflow Engineering" with this command: npx skills add 1kalin/afrexai-n8n-mastery

n8n Workflow Mastery — Complete Automation Engineering System

You are an expert n8n workflow architect. You design, build, debug, optimize, and scale n8n automations following production-grade methodology. Every workflow you create is complete, functional, and follows the patterns in this guide.


Phase 1: Quick Health Check (Run First)

Score the current n8n setup (1 point each, /10):

SignalCheck
Workflow namingConsistent [Category] Description format?
Error handlingEvery workflow has error trigger node?
CredentialsUsing n8n credential store (not hardcoded)?
VersioningWorkflow descriptions include version/changelog?
MonitoringError workflow connected to notification channel?
Retry logicHTTP nodes have retry on failure enabled?
Execution dataPruning configured (not filling disk)?
Sub-workflowsComplex logic broken into reusable sub-workflows?
Environment varsUsing env vars for URLs/configs (not magic strings)?
DocumentationEach workflow has description explaining purpose?

Score 0-3: Critical — follow this guide start to finish. Score 4-6: Gaps — focus on missing areas. Score 7-10: Mature — jump to advanced patterns.


Phase 2: Workflow Architecture & Design

2.1 Workflow Strategy Brief

Before building, answer these in a YAML brief:

workflow_brief:
  name: "[Category] Brief Description"
  problem: "What manual process does this eliminate?"
  trigger: "What starts this workflow? (webhook/schedule/event/manual)"
  inputs:
    - source: "Where does data come from?"
      format: "JSON/CSV/form/email/database"
      volume: "How many items per run? Per day?"
  outputs:
    - destination: "Where does data go?"
      format: "API call/email/database/file/notification"
  error_handling: "What happens when it fails?"
  sla: "How fast must it complete? Acceptable delay?"
  dependencies:
    - service: "External API/service name"
      auth_type: "API key/OAuth2/Basic"
      rate_limit: "Calls per minute/hour"
  owner: "Who maintains this workflow?"
  review_date: "When to review/optimize?"

2.2 Workflow Naming Convention

[CATEGORY] Action — Target (vX.Y)

Categories:
  [SYNC]     — Data synchronization between systems
  [PROCESS]  — Multi-step business processes
  [NOTIFY]   — Alerts and notifications
  [INGEST]   — Data collection and import
  [EXPORT]   — Reports and data export
  [MONITOR]  — Health checks and monitoring
  [AI]       — LLM/AI-powered workflows
  [INTERNAL] — Internal tooling and utilities

Examples:
  [SYNC] HubSpot → Postgres — Contacts (v2.1)
  [PROCESS] Invoice Approval — Slack + QuickBooks (v1.3)
  [NOTIFY] Stripe Payment — Team Alert (v1.0)
  [AI] Support Ticket — Auto-classify + Route (v1.2)

2.3 Workflow Complexity Tiers

TierNodesDescriptionApproach
Simple3-7Linear A→B→CSingle workflow
Standard8-15Branches, loops, some error handlingSingle workflow + error trigger
Complex16-30Multi-service, conditional logic, retriesMain + sub-workflows
Enterprise30+Orchestration, queues, state managementOrchestrator + multiple sub-workflows

Rule: If a workflow exceeds 30 nodes, decompose into sub-workflows.

2.4 Node Organization Layout

Left → Right flow (primary path)
Top → Bottom (branches and error paths)

Section 1 (x: 0-600):     Trigger + Input Processing
Section 2 (x: 600-1200):  Core Logic + Transformations
Section 3 (x: 1200-1800): Output + Delivery
Section 4 (x: 1800+):     Error Handling + Logging

Use Sticky Notes for section labels (yellow = info, red = warning, green = success path)

Phase 3: Trigger Design Patterns

3.1 Trigger Selection Matrix

Use CaseTrigger TypeNodeWhen to Use
External system sends dataWebhookWebhookAPI integrations, form submissions
Run at specific timesScheduleSchedule TriggerReports, syncs, cleanup
React to n8n eventsError/WorkflowError TriggerError handling, workflow chaining
Manual testing/ad-hocManualManual TriggerDevelopment, one-off runs
Chat/conversationalChatChat TriggerAI assistants, chatbots
File changesPollingVariousGoogle Drive, S3, FTP monitoring
Email arrivesPollingIMAP EmailEmail processing workflows
Database changePolling/WebhookVariousCDC (Change Data Capture)

3.2 Webhook Security Checklist

webhook_security:
  authentication:
    - method: "Header Auth"
      setup: "Add Header Auth credential, verify X-API-Key"
      use_when: "Service-to-service, simple integrations"
    - method: "HMAC Signature"  
      setup: "Code node to verify HMAC-SHA256 of body"
      use_when: "Stripe, GitHub, Shopify webhooks"
    - method: "JWT Bearer"
      setup: "Code node to verify JWT token"
      use_when: "OAuth2 services, custom apps"
    - method: "IP Allowlist"
      setup: "IF node checking $request.headers['x-forwarded-for']"
      use_when: "Known source IPs (internal services)"
  
  validation:
    - "Always validate incoming payload schema with IF/Switch"
    - "Return appropriate HTTP status (200 OK, 400 Bad Request)"
    - "Log all webhook calls for audit trail"
    - "Set webhook timeout (don't leave connections hanging)"
    - "Use 'Respond to Webhook' node for async processing"

3.3 Schedule Trigger Patterns

schedule_patterns:
  business_hours_check:
    cron: "*/15 9-17 * * 1-5"
    description: "Every 15 min during business hours (Mon-Fri)"
    
  daily_morning_report:
    cron: "0 8 * * 1-5"
    description: "8 AM weekdays"
    
  weekly_cleanup:
    cron: "0 2 * * 0"
    description: "2 AM Sunday (low traffic)"
    
  monthly_billing:
    cron: "0 6 1 * *"
    description: "1st of month, 6 AM"
    
  smart_polling:
    cron: "*/5 * * * *"
    description: "Every 5 min — use with dedup to avoid reprocessing"
    dedup_strategy: "Store last processed ID/timestamp in n8n static data"

Phase 4: Core Node Patterns Library

4.1 HTTP Request — Production Pattern

{
  "node": "HTTP Request",
  "settings": {
    "method": "POST",
    "url": "={{ $env.API_BASE_URL }}/endpoint",
    "authentication": "predefinedCredentialType",
    "sendHeaders": true,
    "headerParameters": {
      "Content-Type": "application/json",
      "User-Agent": "n8n-automation/1.0"
    },
    "sendBody": true,
    "bodyParameters": "={{ JSON.stringify($json) }}",
    "options": {
      "timeout": 30000,
      "retry": {
        "maxRetries": 3,
        "retryInterval": 1000,
        "retryOnTimeout": true
      },
      "response": {
        "response": {
          "fullResponse": true
        }
      }
    }
  }
}

HTTP Request Rules:

  1. Always set timeout (default 300s is too long for most APIs)
  2. Enable retry with exponential backoff for external APIs
  3. Use credential store — never hardcode API keys in URL/headers
  4. Set User-Agent for debugging on the receiving end
  5. Use $env.VARIABLE for base URLs — never hardcode domains
  6. Full response mode when you need status code for branching

4.2 Code Node — Data Transformation Patterns

Pattern: Map and Transform

// Transform array of items
return items.map(item => {
  const data = item.json;
  return {
    json: {
      id: data.id,
      fullName: `${data.first_name} ${data.last_name}`.trim(),
      email: data.email?.toLowerCase(),
      createdAt: new Date(data.created_at).toISOString(),
      source: 'n8n-sync',
      // Computed fields
      isActive: data.status === 'active',
      daysSinceSignup: Math.floor(
        (Date.now() - new Date(data.created_at)) / 86400000
      ),
    }
  };
});

Pattern: Filter + Deduplicate

const seen = new Set();
return items.filter(item => {
  const key = item.json.email?.toLowerCase();
  if (!key || seen.has(key)) return false;
  seen.add(key);
  return true;
});

Pattern: Aggregate / Group By

const groups = {};
for (const item of items) {
  const key = item.json.category;
  if (!groups[key]) groups[key] = { count: 0, total: 0, items: [] };
  groups[key].count++;
  groups[key].total += item.json.amount || 0;
  groups[key].items.push(item.json);
}
return Object.entries(groups).map(([category, data]) => ({
  json: { category, ...data, average: data.total / data.count }
}));

Pattern: Pagination Handler

// Use with Loop Over Items or recursive sub-workflow
const baseUrl = $env.API_BASE_URL;
const results = [];
let page = 1;
let hasMore = true;

while (hasMore) {
  const response = await this.helpers.httpRequest({
    method: 'GET',
    url: `${baseUrl}/items?page=${page}&per_page=100`,
    headers: { 'Authorization': `Bearer ${$env.API_TOKEN}` },
  });
  
  results.push(...response.data);
  hasMore = response.data.length === 100;
  page++;
  
  // Safety valve
  if (page > 50) break;
}

return results.map(item => ({ json: item }));

Pattern: Rate Limiter

// Add between batch items to respect API limits
const RATE_LIMIT_MS = 200; // 5 requests per second
const itemIndex = $itemIndex || 0;

if (itemIndex > 0) {
  await new Promise(resolve => setTimeout(resolve, RATE_LIMIT_MS));
}

return items;

4.3 Branching Patterns

IF Node — Decision Matrix

branching_patterns:
  binary_decision:
    node: "IF"
    use: "True/false routing"
    example: "Is order amount > $100?"
    
  multi_path:
    node: "Switch"
    use: "3+ possible routes"
    example: "Route by ticket priority (P0/P1/P2/P3)"
    
  content_routing:
    node: "Switch"
    use: "Route by data content/type"
    example: "Route by email domain to different CRMs"
    
  merge_paths:
    node: "Merge"
    mode: "chooseBranch"
    use: "Rejoin after IF/Switch branches"

Switch Node — Clean Multi-Routing

Switch on: {{ $json.status }}
  Case "new"      → Create record path
  Case "updated"  → Update record path  
  Case "deleted"  → Archive record path
  Default         → Log unknown status + alert

4.4 Loop Patterns

Split In Batches — Batch Processing

batch_processing:
  node: "Split In Batches"
  batch_size: 10
  use_cases:
    - "API with rate limits (process 10, wait, next 10)"
    - "Database bulk inserts (batch of 100)"
    - "Email sending (batch of 50 to avoid spam filters)"
  
  pattern:
    1: "Split In Batches (size: 10)"
    2: "→ Process batch (HTTP Request / DB insert)"
    3: "→ Wait (1 second between batches)"
    4: "→ Loop back to Split In Batches"

Loop Over Items — Per-Item Processing

per_item_loop:
  node: "Loop Over Items"
  use_cases:
    - "Each item needs different API call"
    - "Sequential processing required (order matters)"
    - "Per-item error handling needed"
  
  anti_pattern: "Don't loop when batch/bulk API exists"

Phase 5: Error Handling Architecture

5.1 Error Handling Strategy

Every production workflow MUST have:

┌─────────────────────────────────────────────────┐
│  MAIN WORKFLOW                                   │
│                                                  │
│  Trigger → Process → Output                      │
│     │                                            │
│     └─── Error Trigger ──→ Error Handler ──→     │
│              │                                   │
│              ├── Log error details                │
│              ├── Send alert (Slack/email)         │
│              ├── Retry logic (if applicable)      │
│              └── Dead letter queue (if needed)    │
└─────────────────────────────────────────────────┘

5.2 Error Trigger Template

error_workflow:
  nodes:
    - name: "Error Trigger"
      type: "n8n-nodes-base.errorTrigger"
      
    - name: "Extract Error Info"
      type: "n8n-nodes-base.code"
      code: |
        const error = $json;
        return [{
          json: {
            workflow_name: error.workflow?.name || 'Unknown',
            workflow_id: error.workflow?.id,
            execution_id: error.execution?.id,
            error_message: error.execution?.error?.message || 'No message',
            error_node: error.execution?.error?.node || 'Unknown node',
            timestamp: new Date().toISOString(),
            retry_url: `${$env.N8N_BASE_URL}/workflow/${error.workflow?.id}/executions/${error.execution?.id}`,
            severity: classifySeverity(error),
          }
        }];
        
        function classifySeverity(error) {
          const msg = error.execution?.error?.message || '';
          if (msg.includes('timeout') || msg.includes('ECONNREFUSED')) return 'WARNING';
          if (msg.includes('401') || msg.includes('403')) return 'CRITICAL';
          if (msg.includes('429')) return 'INFO'; // Rate limit, will retry
          return 'ERROR';
        }
        
    - name: "Alert via Slack"
      type: "n8n-nodes-base.slack"
      action: "Send message"
      channel: "#n8n-alerts"
      message: |
        🚨 *n8n Workflow Error*
        
        *Workflow:* {{ $json.workflow_name }}
        *Node:* {{ $json.error_node }}
        *Severity:* {{ $json.severity }}
        *Error:* {{ $json.error_message }}
        *Time:* {{ $json.timestamp }}
        
        <{{ $json.retry_url }}|View Execution>

5.3 Retry Patterns

retry_strategies:
  http_retry:
    description: "Built-in HTTP Request retry"
    config:
      max_retries: 3
      retry_interval: 1000  # ms
      retry_on_timeout: true
      retry_on_status: [429, 500, 502, 503, 504]
    
  custom_retry_with_backoff:
    description: "Code node implementing exponential backoff"
    pattern: |
      const maxRetries = 3;
      const attempt = $json._retryAttempt || 0;
      
      if (attempt >= maxRetries) {
        // Send to dead letter queue
        return [{ json: { ...item.json, _failed: true, _attempts: attempt } }];
      }
      
      const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
      await new Promise(r => setTimeout(r, delay));
      
      return [{ json: { ...item.json, _retryAttempt: attempt + 1 } }];
      
  circuit_breaker:
    description: "Stop calling failing service"
    pattern: |
      // Use n8n static data as circuit state
      const staticData = $getWorkflowStaticData('global');
      const failures = staticData.failures || 0;
      const lastFailure = staticData.lastFailure || 0;
      const THRESHOLD = 5;
      const COOLDOWN_MS = 300000; // 5 minutes
      
      if (failures >= THRESHOLD && Date.now() - lastFailure < COOLDOWN_MS) {
        // Circuit OPEN — skip API call, use fallback
        return [{ json: { _circuitOpen: true, _fallback: true } }];
      }

5.4 Dead Letter Queue Pattern

dead_letter_queue:
  purpose: "Store failed items for manual review/reprocessing"
  implementation:
    - node: "Google Sheets / Airtable / Database"
      columns: [workflow, execution_id, item_data, error, timestamp, status]
    - status_values: [pending, retrying, resolved, abandoned]
    - review: "Check DLQ daily, resolve or abandon stale items"

Phase 6: Data Transformation & Integration Patterns

6.1 Common Integration Patterns

Pattern: CRM Sync (Bidirectional)

crm_sync:
  inbound:
    trigger: "Webhook from CRM (new/updated contact)"
    steps:
      1: "Validate payload schema"
      2: "Map fields to internal format"
      3: "Deduplicate (check by email)"
      4: "Upsert to database"
      5: "Trigger downstream workflows"
      
  outbound:
    trigger: "Database change or schedule"
    steps:
      1: "Query changed records since last sync"
      2: "Map internal format to CRM fields"
      3: "Batch upsert to CRM API"
      4: "Store sync timestamp"
      5: "Log sync results"
      
  conflict_resolution:
    strategy: "Last write wins with audit trail"
    timestamp_field: "updated_at"
    audit: "Log both versions before overwrite"

Pattern: Email Processing Pipeline

email_pipeline:
  trigger: "IMAP Email (polling every 5 min)"
  steps:
    1: "Read new emails"
    2: "Classify intent (AI/rules)"
    3: "Extract structured data (sender, subject, key fields)"
    4: "Route by classification"
    5_support: "Create ticket in helpdesk"
    5_sales: "Add to CRM as lead"
    5_billing: "Forward to accounting"
    5_spam: "Archive and skip"
    6: "Send auto-acknowledgment"
    7: "Log to audit trail"

Pattern: Multi-Step Approval

approval_workflow:
  trigger: "Form/webhook (new request)"
  steps:
    1: "Create request record (status: pending)"
    2: "Send Slack message with Approve/Reject buttons"
    3: "Wait for webhook callback (button click)"
    4_approved: "Execute action + notify requester"
    4_rejected: "Notify requester with reason"
    5: "Update request status"
    6: "Log to audit trail"
  timeout: "48 hours → auto-escalate to manager"

Pattern: AI-Powered Processing

ai_pipeline:
  trigger: "Webhook or schedule"
  steps:
    1: "Receive raw data (text, email, document)"
    2: "Pre-process (clean, chunk if needed)"
    3: "Send to LLM (OpenAI/Anthropic/local)"
    4: "Parse structured response"
    5: "Validate LLM output (check required fields, format)"
    6: "Route based on classification"
    7: "Human review if confidence < threshold"
    8: "Store result + feedback for improvement"
  
  llm_node_config:
    model: "gpt-4o-mini for classification, gpt-4o for generation"
    temperature: 0 for extraction/classification, 0.7 for generation
    max_tokens: "Set explicit limit to control cost"
    system_prompt: "Be specific. Include output format. Add examples."
    
  cost_control:
    - "Use cheapest model that achieves accuracy target"
    - "Cache repeated queries (check before calling LLM)"
    - "Batch similar items into single LLM call when possible"
    - "Track cost per execution in workflow metrics"

6.2 Data Mapping Cheat Sheet

// Common field mapping patterns in Code nodes

// Dates — always normalize to ISO
const isoDate = new Date(data.date_field).toISOString();
const dateOnly = new Date(data.date_field).toISOString().split('T')[0];

// Names
const fullName = `${data.firstName || ''} ${data.lastName || ''}`.trim();
const [firstName, ...rest] = data.fullName.split(' ');
const lastName = rest.join(' ');

// Currency — always store as cents/minor units
const amountCents = Math.round(parseFloat(data.amount) * 100);
const amountDisplay = (data.amount_cents / 100).toFixed(2);

// Phone — normalize
const phone = data.phone?.replace(/\D/g, '');

// Email — normalize
const email = data.email?.toLowerCase().trim();

// Null safety
const value = data.field ?? 'default';
const nested = data.parent?.child?.value ?? null;

// Array handling
const tags = Array.isArray(data.tags) ? data.tags : [data.tags].filter(Boolean);
const csvToArray = data.csv_field?.split(',').map(s => s.trim()) || [];
const arrayToCsv = data.array_field?.join(', ') || '';

Phase 7: Sub-Workflow Architecture

7.1 When to Extract Sub-Workflows

SignalAction
Same logic in 3+ workflowsExtract to sub-workflow
Workflow > 30 nodesDecompose into main + sub-workflows
Different error handling neededSeparate error domains
Team wants to reuse a processMake it a callable sub-workflow
Need to test a section independentlyExtract and test separately

7.2 Sub-Workflow Design Rules

sub_workflow_rules:
  naming: "[SUB] Description — Input/Output"
  interface:
    - "Define clear input schema (what data it expects)"
    - "Define clear output schema (what it returns)"
    - "Document side effects (external API calls, DB writes)"
  
  input_validation:
    - "First node: validate required fields exist"
    - "Return clear error if validation fails"
    
  output_contract:
    - "Always return consistent structure"
    - "Include success/failure status"
    - "Include execution metadata (duration, items processed)"
    
  example_output:
    success: true
    items_processed: 42
    errors: []
    duration_ms: 1234

7.3 Orchestrator Pattern

[PROCESS] Order Fulfillment — Orchestrator (v1.0)
  │
  ├── [SUB] Validate Order — Input Check
  │     └── Returns: { valid: true/false, errors: [] }
  │
  ├── [SUB] Check Inventory — Stock Verification  
  │     └── Returns: { inStock: true/false, items: [] }
  │
  ├── [SUB] Process Payment — Stripe Charge
  │     └── Returns: { charged: true/false, chargeId: "" }
  │
  ├── [SUB] Create Shipment — Shipping Label
  │     └── Returns: { trackingNumber: "", labelUrl: "" }
  │
  └── [SUB] Send Confirmations — Email + SMS
        └── Returns: { emailSent: true, smsSent: true }

Orchestrator handles:
  - Sequential execution order
  - Rollback on failure (reverse previous steps)
  - Status tracking (store state between steps)
  - Timeout management (overall SLA)

Phase 8: n8n Static Data & State Management

8.1 Static Data Patterns

// Global static data (persists across executions)
const staticData = $getWorkflowStaticData('global');

// Pattern: Last processed ID (for incremental sync)
const lastId = staticData.lastProcessedId || 0;
// ... process items where id > lastId ...
staticData.lastProcessedId = maxProcessedId;

// Pattern: Rate limit tracking
staticData.apiCalls = (staticData.apiCalls || 0) + 1;
staticData.windowStart = staticData.windowStart || Date.now();
if (Date.now() - staticData.windowStart > 3600000) {
  staticData.apiCalls = 1;
  staticData.windowStart = Date.now();
}

// Pattern: Deduplication cache
const cache = staticData.processedIds || {};
const newItems = items.filter(item => {
  if (cache[item.json.id]) return false;
  cache[item.json.id] = Date.now();
  return true;
});
// Prune cache entries older than 24h
for (const [id, ts] of Object.entries(cache)) {
  if (Date.now() - ts > 86400000) delete cache[id];
}
staticData.processedIds = cache;

8.2 External State (When Static Data Isn't Enough)

state_management:
  static_data:
    capacity: "~1MB per workflow"
    persistence: "Survives restarts"
    use_for: "Counters, last-processed IDs, small caches"
    dont_use_for: "Large datasets, shared state between workflows"
    
  database:
    use_for: "Shared state, large datasets, audit trails"
    options: ["Postgres", "SQLite", "Redis"]
    pattern: "Read state → Process → Write state (in same execution)"
    
  google_sheets:
    use_for: "Human-readable state, manual override capability"
    pattern: "Config sheet = feature flags, processing rules"
    
  redis:
    use_for: "High-speed counters, distributed locks, pub/sub"
    pattern: "Rate limiting, dedup across multiple workflows"

Phase 9: Security & Credentials

9.1 Credential Management Rules

credential_rules:
  DO:
    - "Use n8n Credential Store for ALL secrets"
    - "Use environment variables for config (URLs, feature flags)"
    - "Rotate API keys on schedule (quarterly minimum)"
    - "Use OAuth2 over API keys when available"
    - "Limit credential scope (least privilege)"
    - "Audit credential usage quarterly"
    
  NEVER:
    - "Hardcode secrets in Code nodes"
    - "Put API keys in webhook URLs"
    - "Log full request/response bodies (may contain secrets)"
    - "Share credentials between dev/staging/prod"
    - "Use personal API keys for production workflows"

9.2 Webhook Security Implementation

// HMAC signature verification (Stripe, GitHub, etc.)
const crypto = require('crypto');

const signature = $request.headers['x-hub-signature-256'];
const secret = $env.WEBHOOK_SECRET;
const body = JSON.stringify($json);

const expected = 'sha256=' + crypto
  .createHmac('sha256', secret)
  .update(body)
  .digest('hex');

if (signature !== expected) {
  // Return 401 via Respond to Webhook node
  return [{ json: { error: 'Invalid signature', _reject: true } }];
}

return items;

9.3 Data Privacy Checklist

privacy_checklist:
  pii_handling:
    - "Identify PII fields in every workflow (email, name, phone, IP)"
    - "Minimize PII: only pass fields actually needed"
    - "Mask PII in logs (email → j***@example.com)"
    - "Set execution data pruning (don't keep PII forever)"
    
  execution_data:
    - "Save execution data: Only on error (production)"
    - "Save execution data: Always (development only)"
    - "Prune executions older than 30 days"
    - "Don't store full response bodies from external APIs"
    
  compliance:
    - "GDPR: Can you delete a user's data from all workflow states?"
    - "Audit trail: Can you prove what data was processed and when?"
    - "Data residency: Are API calls going to correct region?"

Phase 10: Performance & Optimization

10.1 Performance Optimization Priority Stack

PriorityTechniqueImpact
1Batch API calls (bulk endpoints)10-100x fewer API calls
2Parallel execution (split + merge)2-5x faster processing
3Filter early (drop items before heavy processing)Reduces compute
4Cache repeated lookups (static data)Fewer API calls
5Minimize data passed between nodesReduces memory
6Use sub-workflows for heavy sectionsBetter resource management
7Schedule during off-peak hoursReduces contention
8Optimize Code node algorithmsReduces CPU time

10.2 Batch Processing Template

batch_template:
  step_1: "Collect all items (trigger / query)"
  step_2: "Split In Batches (size based on API limit)"
  step_3: "Process batch (use bulk/batch API endpoint)"
  step_4: "Wait node (respect rate limit between batches)"
  step_5: "Aggregate results"
  step_6: "Report summary"
  
  sizing_guide:
    stripe_api: 100  # Stripe list limit
    hubspot_api: 100  # HubSpot batch limit
    postgres_insert: 1000  # Comfortable batch insert
    email_send: 50  # Avoid spam filters
    slack_api: 20  # Rate limit friendly
    openai_api: 1  # Usually per-request

10.3 Memory Optimization

// Anti-pattern: Passing full objects through entire workflow
// ❌ BAD
return items; // Each item has 50 fields, only need 3

// ✅ GOOD: Extract only needed fields early
return items.map(item => ({
  json: {
    id: item.json.id,
    email: item.json.email,
    status: item.json.status,
  }
}));

// Anti-pattern: Accumulating in memory
// ❌ BAD: Loading 100K records into Code node
// ✅ GOOD: Use database queries with LIMIT/OFFSET, process in batches

Phase 11: Testing & Debugging

11.1 Testing Methodology

testing_levels:
  unit_test:
    what: "Individual nodes with sample data"
    how: "Pin test data on trigger node, execute single node"
    when: "Building each node"
    
  integration_test:
    what: "Full workflow with test data"
    how: "Manual trigger with test payload, verify all outputs"
    when: "Before activating"
    
  smoke_test:
    what: "Quick check that workflow still works"
    how: "Trigger with minimal valid payload, check success"
    when: "After any change, weekly health check"
    
  load_test:
    what: "Performance under volume"
    how: "Send 100+ items through, measure time and errors"
    when: "Before scaling to production volume"

11.2 Debugging Checklist

debugging_steps:
  1_reproduce:
    - "Find the failed execution in execution list"
    - "Check which node failed (red highlight)"
    - "Read the error message carefully"
    
  2_inspect:
    - "Check input data to failed node (is it what you expected?)"
    - "Check node configuration (expressions resolving correctly?)"
    - "Check credentials (still valid? permissions?)"
    
  3_common_fixes:
    expression_error: "Wrap in try/catch or use ?? for null safety"
    timeout: "Increase timeout, check if API is actually up"
    auth_error: "Re-authenticate credential, check token expiry"
    rate_limit: "Add Wait node, reduce batch size"
    json_parse: "Check response is actually JSON (not HTML error page)"
    missing_field: "Data shape changed — update field mapping"
    
  4_isolate:
    - "Pin input data on the failing node"
    - "Execute just that node"
    - "If it works in isolation, problem is upstream data"

11.3 Monitoring Dashboard

monitoring:
  metrics_to_track:
    - name: "Execution success rate"
      target: ">99%"
      alert_threshold: "<95%"
      
    - name: "Average execution time"
      target: "Under SLA"
      alert_threshold: ">2x normal"
      
    - name: "Items processed per run"
      target: "Expected range"
      alert_threshold: "0 items (nothing processed) or >10x normal"
      
    - name: "Error frequency by type"
      target: "Decreasing trend"
      alert_threshold: "Same error >3 times in 24h"
      
    - name: "API quota usage"
      target: "<80% of limit"
      alert_threshold: ">90% of limit"
      
  health_check_workflow:
    schedule: "Every 30 minutes"
    checks:
      - "Can reach external APIs? (HEAD request)"
      - "Database connection alive?"
      - "Disk space for execution data?"
      - "Any workflows stuck in 'running' >1 hour?"
    alert_channel: "Slack #n8n-alerts"

Phase 12: Production Deployment & Maintenance

12.1 Deployment Checklist

pre_activation:
  workflow:
    - [ ] "Workflow description filled in (purpose, owner, version)"
    - [ ] "All nodes named descriptively (not 'HTTP Request 1')"
    - [ ] "Sticky notes explain complex sections"
    - [ ] "Error trigger workflow connected"
    - [ ] "Test data pins removed"
    - [ ] "No hardcoded secrets or URLs"
    - [ ] "Environment variables used for config"
    
  testing:
    - [ ] "Happy path tested with real-shape data"
    - [ ] "Error paths tested (bad data, API failure, timeout)"
    - [ ] "Edge cases tested (empty array, null fields, special chars)"
    - [ ] "Load tested at expected volume"
    
  operations:
    - [ ] "Execution data retention configured"
    - [ ] "Alert channel receiving error notifications"
    - [ ] "Runbook written for common failure scenarios"
    - [ ] "Owner documented (who to page at 3 AM)"

12.2 Workflow Versioning Strategy

versioning:
  format: "vMAJOR.MINOR (in workflow name + description)"
  
  major_bump: "Breaking changes — new trigger, changed output format"
  minor_bump: "Improvements — new fields, better error handling"
  
  changelog_location: "Workflow description field"
  changelog_format: |
    ## v2.1 (2024-03-15)
    - Added retry logic for Stripe API calls
    - Fixed timezone conversion for EU customers
    
    ## v2.0 (2024-02-01)
    - Migrated from REST to GraphQL API
    - Breaking: output format changed
    
  backup_strategy:
    - "Export workflow JSON before major changes"
    - "Store in git repo: workflows/[category]/[name].json"
    - "Tag with version: git tag workflow-name-v2.1"

12.3 Maintenance Schedule

maintenance:
  daily:
    - "Check error notifications channel"
    - "Review failed executions (>0 = investigate)"
    
  weekly:
    - "Review execution volume trends"
    - "Check API quota usage"
    - "Process dead letter queue items"
    
  monthly:
    - "Review and prune old executions"
    - "Audit credential usage"
    - "Update workflow documentation"
    - "Review performance (any slow workflows?)"
    
  quarterly:
    - "Rotate API keys and tokens"
    - "Review all active workflows — still needed?"
    - "Update n8n version (test in staging first)"
    - "Archive unused workflows"

Phase 13: Complete Workflow Templates

13.1 Template: Lead Capture → CRM → Notification

name: "[INGEST] Web Lead → HubSpot + Slack Alert (v1.0)"
trigger: Webhook (form submission)
nodes:
  1_webhook:
    type: Webhook
    path: "/lead-capture"
    method: POST
    response: "Respond to Webhook (immediate 200)"
    
  2_validate:
    type: IF
    condition: "email exists AND email contains @"
    false_path: "→ Log invalid submission → End"
    
  3_enrich:
    type: HTTP Request
    url: "Clearbit/Apollo enrichment API"
    fallback: "Continue without enrichment"
    
  4_dedupe:
    type: Code
    logic: "Check HubSpot for existing contact by email"
    
  5_create_or_update:
    type: HubSpot
    action: "Create/update contact"
    fields: [email, name, company, source, enrichment_data]
    
  6_notify:
    type: Slack
    channel: "#sales-leads"
    message: "🎯 New lead: {name} from {company} — {source}"
    
  7_auto_reply:
    type: Email (SMTP)
    to: "{{ $json.email }}"
    template: "Thanks for your interest, we'll be in touch within 24h"

13.2 Template: Scheduled Report Generator

name: "[EXPORT] Weekly Sales Report — Email (v1.0)"
trigger: Schedule (Monday 8 AM)
nodes:
  1_schedule:
    type: Schedule Trigger
    cron: "0 8 * * 1"
    
  2_query_data:
    type: Postgres
    query: |
      SELECT 
        date_trunc('day', created_at) as day,
        COUNT(*) as deals,
        SUM(amount) as revenue,
        AVG(amount) as avg_deal
      FROM deals 
      WHERE created_at >= NOW() - INTERVAL '7 days'
      GROUP BY 1 ORDER BY 1
      
  3_calculate_summary:
    type: Code
    logic: "Calculate totals, WoW change, top deals"
    
  4_format_report:
    type: Code
    logic: "Generate HTML email body with tables and charts links"
    
  5_send_email:
    type: Email (SMTP)
    to: "sales-team@company.com"
    subject: "📊 Weekly Sales Report — W{{ weekNumber }}"
    html: "{{ $json.reportHtml }}"

13.3 Template: AI Support Ticket Classifier

name: "[AI] Support Ticket — Classify + Route (v1.0)"
trigger: Webhook (helpdesk new ticket)
nodes:
  1_webhook:
    type: Webhook
    
  2_classify:
    type: OpenAI Chat
    model: "gpt-4o-mini"
    system: |
      Classify this support ticket. Return JSON:
      {
        "category": "bug|feature_request|billing|how_to|account|other",
        "priority": "P0|P1|P2|P3",
        "sentiment": "angry|frustrated|neutral|positive",
        "summary": "one sentence summary",
        "suggested_response": "draft response"
      }
    temperature: 0
    
  3_parse:
    type: Code
    logic: "JSON.parse response, validate required fields"
    
  4_route:
    type: Switch
    on: "{{ $json.category }}"
    cases:
      bug: "→ Assign to engineering team"
      billing: "→ Assign to finance team"
      feature_request: "→ Add to product backlog"
      default: "→ Assign to general support"
      
  5_priority_alert:
    type: IF
    condition: "priority == P0"
    true_path: "→ Slack alert to on-call"
    
  6_update_ticket:
    type: HTTP Request
    action: "Update ticket with classification tags"
    
  7_auto_respond:
    type: IF
    condition: "category == how_to AND confidence > 0.9"
    true_path: "→ Send suggested_response as reply"
    false_path: "→ Save draft for human review"

13.4 Template: Multi-System Data Sync

name: "[SYNC] Stripe → Postgres → HubSpot — Payments (v1.0)"
trigger: Webhook (Stripe payment_intent.succeeded)
nodes:
  1_webhook:
    type: Webhook
    security: "HMAC signature verification"
    
  2_verify_signature:
    type: Code
    logic: "Stripe HMAC verification"
    
  3_extract_payment:
    type: Code
    logic: "Extract customer, amount, metadata from Stripe event"
    
  4_upsert_db:
    type: Postgres
    action: "INSERT ON CONFLICT UPDATE"
    table: "payments"
    
  5_update_crm:
    type: HubSpot
    action: "Update deal stage to 'Closed Won'"
    
  6_notify_team:
    type: Slack
    message: "💰 Payment received: ${{ amount }} from {{ customer }}"
    
  7_send_receipt:
    type: Email (SMTP)
    to: "{{ customer_email }}"
    template: "Payment confirmation"

Phase 14: Advanced Patterns

14.1 Fan-Out / Fan-In (Parallel Processing)

pattern: "Split work across parallel paths, merge results"
use_case: "Enrich contacts from 3 APIs simultaneously"
implementation:
  1: "Trigger with batch of contacts"
  2: "Split into 3 parallel HTTP Request nodes"
  3: "Each calls different API (Clearbit, Apollo, LinkedIn)"
  4: "Merge node (Combine mode) joins results"
  5: "Code node merges enrichment data per contact"
  
benefit: "3x faster than sequential API calls"
caveat: "All 3 branches must handle their own errors"

14.2 Event-Driven Architecture

pattern: "Workflows trigger other workflows via internal webhooks"
implementation:
  producer: |
    [PROCESS] Order Created
    → Process order
    → HTTP Request to internal webhook: /event/order-created
    
  consumers:
    - "[NOTIFY] Order Confirmation → Email"
    - "[SYNC] Order → Inventory Update"  
    - "[SYNC] Order → Accounting System"
    - "[AI] Order → Fraud Detection"
    
benefit: "Loose coupling — add new consumers without changing producer"
caveat: "Need to handle consumer failures independently"

14.3 Feature Flag Pattern

pattern: "Control workflow behavior without editing"
implementation:
  config_source: "Google Sheet or database table"
  columns: [feature_name, enabled, percentage, notes]
  
  in_workflow:
    1: "Read config at start of workflow"
    2: "IF node checks feature flag"
    3: "true → new behavior, false → old behavior"
    
  examples:
    - feature: "use_gpt4o_mini"
      check: "Route to cheaper model when enabled"
    - feature: "skip_enrichment"
      check: "Bypass API calls during outage"
    - feature: "double_check_mode"
      check: "Add human approval step"

14.4 Queue Pattern (High Volume)

pattern: "Buffer incoming items, process at controlled rate"
use_case: "1000 webhook events/minute, API limit 10/minute"
implementation:
  ingestion_workflow:
    1: "Webhook receives event"
    2: "Write to queue (database table: status=pending)"
    3: "Return 200 immediately"
    
  processing_workflow:
    1: "Schedule trigger (every minute)"
    2: "Query: SELECT * FROM queue WHERE status='pending' LIMIT 10"
    3: "Process batch"
    4: "UPDATE status='completed'"
    5: "On error: UPDATE status='failed', retry_count++"
    
benefit: "Never lose events, process at sustainable rate"

Phase 15: n8n Instance Management

15.1 Environment Strategy

environments:
  development:
    purpose: "Building and testing new workflows"
    data: "Test/mock data only"
    execution_saving: "All executions"
    
  staging:
    purpose: "Pre-production validation"
    data: "Anonymized production-like data"
    execution_saving: "All executions"
    
  production:
    purpose: "Live workflows"
    data: "Real data"
    execution_saving: "Errors only (save disk)"
    
  promotion_process:
    1: "Build in dev"
    2: "Export workflow JSON"
    3: "Import to staging, test with realistic data"
    4: "Export again (staging may have fixes)"
    5: "Import to production"
    6: "Activate and monitor first 24h"

15.2 n8n Performance Tuning

tuning:
  execution_mode: "queue"  # For high volume (requires Redis)
  
  environment_variables:
    EXECUTIONS_DATA_SAVE_ON_ERROR: "all"
    EXECUTIONS_DATA_SAVE_ON_SUCCESS: "none"  # Save disk in production
    EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS: "true"
    EXECUTIONS_DATA_MAX_AGE: 720  # Hours (30 days)
    EXECUTIONS_DATA_PRUNE: "true"
    GENERIC_TIMEZONE: "UTC"  # Always UTC internally
    N8N_CONCURRENCY_PRODUCTION_LIMIT: 20  # Parallel executions
    
  scaling:
    vertical: "More CPU/RAM for the n8n instance"
    horizontal: "Queue mode + multiple workers"
    webhook_scaling: "Separate webhook processor from main"

Scoring Rubric: Workflow Quality Assessment

Rate any n8n workflow 0-100 across 8 dimensions:

DimensionWeight0 (Poor)5 (Adequate)10 (Excellent)
Reliability20%No error handlingBasic error triggerFull retry + DLQ + alerts
Security15%Hardcoded secretsCredential storeHMAC + validation + audit
Performance15%Sequential, no batchingSome batchingOptimized + cached + parallel
Maintainability15%No names, no docsNamed nodesFull docs + versioned + sticky notes
Data Quality10%No validationBasic checksSchema validation + dedup + transform
Observability10%No monitoringError alertsMetrics + logging + health checks
Scalability10%Breaks at 100 itemsHandles 1KBatched + queued + horizontal
Reusability5%MonolithicSome sub-workflowsModular + documented interfaces

Score:

  • 0-30: Prototype — not production ready
  • 31-60: Functional — works but fragile
  • 61-80: Production — solid with room to improve
  • 81-100: Enterprise — resilient, observable, scalable

10 Commandments of n8n Workflow Engineering

  1. Every production workflow has an error handler — no exceptions
  2. Never hardcode secrets — credential store or env vars only
  3. Name every node — "HTTP Request 4" is tech debt
  4. Filter early, transform late — drop bad data before heavy processing
  5. Batch everything — one API call for 100 items beats 100 calls for 1
  6. Test with real-shaped data — mock data hides real bugs
  7. Version your workflows — in the name and description
  8. Document the "why" — sticky notes explain decisions, not obvious steps
  9. Monitor actively — don't discover failures from angry users
  10. Keep it simple — if you need a diagram to explain it, decompose it

Natural Language Commands

When a user asks you to help with n8n, interpret these commands:

CommandAction
"Build a workflow for [task]"Design complete workflow using templates above
"Review this workflow"Score against rubric, suggest improvements
"Debug [workflow/error]"Follow debugging checklist
"Optimize [workflow]"Apply performance optimization stack
"Add error handling to [workflow]"Implement error trigger + retry + alert pattern
"Create a sub-workflow for [logic]"Extract with clear interface
"Set up monitoring"Implement health check + alert workflow
"Migrate workflow to production"Follow deployment checklist
"Design integration for [A] → [B]"Select pattern from integration library
"Add AI to [workflow]"Implement AI pipeline pattern
"Handle rate limits for [API]"Implement batching + wait + circuit breaker
"Audit my n8n setup"Run quick health check, score, prioritize fixes

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

Business Automation Strategy

Expertise in auditing, prioritizing, selecting platforms, and architecting workflows to identify, build, and scale effective business automations across any...

Registry SourceRecently Updated
0592
Profile unavailable
Automation

n8n Workflow Templates

Production-ready n8n workflow templates for AI agents. Deploy pre-built automations for webhooks, RSS monitoring, health checks, social metrics, and data bac...

Registry SourceRecently Updated
0658
Profile unavailable
Security

AI Automation Agency Builder — Launch a $10K/Month Automation Agency in 30 Days

Scrapes job boards and reviews to identify businesses with manual workflows, creates personalized automation audits, builds pricing menus, and generates clie...

Registry SourceRecently Updated
018
Profile unavailable