n8n Workflow Automation: Complete Business Process Integration
n8n is a powerful, self-hosted workflow automation tool that connects various services and automates complex business processes. This comprehensive guide covers everything from basic workflow creation to advanced custom integrations and scalable automation patterns.
Why Choose n8n for Business Automation?
- Visual Workflow Builder: Intuitive drag-and-drop interface
- Self-Hosted: Complete data control and privacy
- Extensive Integrations: 400+ pre-built nodes for popular services
- Custom Nodes: Build your own integrations
- Fair-Code License: Free for self-hosting with commercial options
Step 1: n8n Installation and Setup
Install n8n with Docker for production use:
# Create n8n directory structure
mkdir -p n8n-automation/{data,logs,custom-nodes,workflows}
cd n8n-automation
# Create environment file
cat > .env << 'EOF'
# n8n Configuration
N8N_HOST=0.0.0.0
N8N_PORT=5678
N8N_PROTOCOL=https
NODE_ENV=production
WEBHOOK_URL=https://n8n.yourdomain.com
GENERIC_TIMEZONE=UTC
# Security
N8N_ENCRYPTION_KEY=your-very-long-encryption-key-here
N8N_USER_MANAGEMENT_JWT_SECRET=your-jwt-secret-here
# Database
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=postgres
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=n8n
DB_POSTGRESDB_PASSWORD=n8n_secure_password
# Email Configuration
N8N_EMAIL_MODE=smtp
N8N_SMTP_HOST=smtp.gmail.com
N8N_SMTP_PORT=587
N8N_SMTP_USER=your-email@gmail.com
N8N_SMTP_PASS=your-app-password
N8N_SMTP_SENDER=noreply@yourdomain.com
# Logging
N8N_LOG_LEVEL=info
N8N_LOG_OUTPUT=console,file
N8N_LOG_FILE_LOCATION=/data/logs/
N8N_LOG_FILE_COUNT_MAX=100
N8N_LOG_FILE_SIZE_MAX=16
# Advanced Settings
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=336
N8N_METRICS=true
EOF
Create Docker Compose configuration:
version: "3.8"
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
env_file:
- .env
environment:
- N8N_USER_FOLDER=/data
- N8N_CUSTOM_EXTENSIONS=/data/custom-nodes
volumes:
- ./data:/data
- ./custom-nodes:/data/custom-nodes
- ./logs:/data/logs
- ./workflows:/data/workflows
depends_on:
postgres:
condition: service_healthy
labels:
- "traefik.enable=true"
- "traefik.http.routers.n8n.rule=Host(`n8n.yourdomain.com`)"
- "traefik.http.routers.n8n.tls=true"
- "traefik.http.routers.n8n.tls.certresolver=letsencrypt"
- "traefik.http.services.n8n.loadbalancer.server.port=5678"
networks:
- n8n-network
healthcheck:
test:
[
"CMD",
"wget",
"--no-verbose",
"--tries=1",
"--spider",
"http://localhost:5678/healthz",
]
interval: 30s
timeout: 10s
retries: 3
postgres:
image: postgres:15-alpine
container_name: n8n-postgres
restart: unless-stopped
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=n8n_secure_password
- POSTGRES_INITDB_ARGS=--auth-host=scram-sha-256
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- n8n-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U n8n -d n8n"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: n8n-redis
restart: unless-stopped
command: redis-server --appendonly yes --requirepass redis_secure_password
volumes:
- redis_data:/data
networks:
- n8n-network
healthcheck:
test: ["CMD", "redis-cli", "--pass", "redis_secure_password", "ping"]
interval: 10s
timeout: 5s
retries: 3
volumes:
postgres_data:
redis_data:
networks:
n8n-network:
driver: bridge
docker-compose.yml
Step 2: Customer Onboarding Automation Workflow
Create a comprehensive customer onboarding workflow:
{
"name": "Customer Onboarding Automation",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "new-customer",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook-trigger",
"name": "New Customer Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [240, 300],
"webhookId": "customer-onboarding-webhook"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"leftValue": "={{ $json.email }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "isNotEmpty"
}
},
{
"leftValue": "={{ $json.name }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "isNotEmpty"
}
}
],
"combinator": "and"
}
},
"id": "validate-input",
"name": "Validate Customer Data",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [460, 300]
},
{
"parameters": {
"method": "POST",
"url": "https://api.airtable.com/v0/{{ $vars.AIRTABLE_BASE_ID }}/Customers",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "airtableTokenApi",
"headers": {
"Content-Type": "application/json"
},
"body": {
"fields": {
"Name": "={{ $json.name }}",
"Email": "={{ $json.email }}",
"Company": "={{ $json.company || 'N/A' }}",
"Phone": "={{ $json.phone || '' }}",
"Status": "New",
"Created Date": "={{ $now }}",
"Source": "={{ $json.source || 'Website' }}"
}
}
},
"id": "create-customer-record",
"name": "Create Customer in Airtable",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [680, 200],
"credentials": {
"airtableTokenApi": {
"id": "airtable_api_key",
"name": "Airtable API"
}
}
},
{
"parameters": {
"operation": "create",
"userId": "={{ $json.email }}",
"additionalFields": {
"firstName": "={{ $json.name.split(' ')[0] }}",
"lastName": "={{ $json.name.split(' ').slice(1).join(' ') }}",
"phone": "={{ $json.phone }}",
"company": "={{ $json.company }}"
}
},
"id": "create-crm-contact",
"name": "Create Contact in CRM",
"type": "n8n-nodes-base.hubspot",
"typeVersion": 2,
"position": [680, 350],
"credentials": {
"hubspotApi": {
"id": "hubspot_api_key",
"name": "HubSpot API"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://api.intercom.io/contacts",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "intercomApi",
"headers": {
"Accept": "application/json",
"Content-Type": "application/json"
},
"body": {
"email": "={{ $json.email }}",
"name": "={{ $json.name }}",
"custom_attributes": {
"company": "={{ $json.company }}",
"source": "={{ $json.source }}",
"onboarding_status": "started"
}
}
},
"id": "create-intercom-contact",
"name": "Create Contact in Intercom",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [680, 500],
"credentials": {
"intercomApi": {
"id": "intercom_api_key",
"name": "Intercom API"
}
}
},
{
"parameters": {
"operation": "send",
"resource": "email",
"to": "={{ $json.email }}",
"subject": "Welcome to {{ $vars.COMPANY_NAME }}! 🎉",
"message": "<!DOCTYPE html><html><head><style>body{font-family:Arial,sans-serif;line-height:1.6;color:#333}.container{max-width:600px;margin:0 auto;padding:20px}.header{background:#007bff;color:white;padding:20px;text-align:center}.content{padding:20px}.button{background:#007bff;color:white;padding:12px 24px;text-decoration:none;border-radius:5px;display:inline-block;margin:10px 0}</style></head><body><div class='container'><div class='header'><h1>Welcome to {{ $vars.COMPANY_NAME }}!</h1></div><div class='content'><p>Hi {{ $json.name.split(' ')[0] }},</p><p>Thank you for joining {{ $vars.COMPANY_NAME }}! We're excited to have you on board.</p><p>Here's what happens next:</p><ul><li>📋 Complete your profile setup</li><li>🎯 Schedule your onboarding call</li><li>📚 Access our getting started guide</li><li>💬 Join our community</li></ul><p><a href='{{ $vars.ONBOARDING_URL }}' class='button'>Get Started Now</a></p><p>If you have any questions, feel free to reach out to our support team.</p><p>Best regards,<br>The {{ $vars.COMPANY_NAME }} Team</p></div></div></body></html>",
"options": {
"allowUnauthorizedCerts": false,
"ccEmail": "",
"bccEmail": "onboarding@{{ $vars.COMPANY_DOMAIN }}",
"replyTo": "support@{{ $vars.COMPANY_DOMAIN }}"
}
},
"id": "send-welcome-email",
"name": "Send Welcome Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [900, 200],
"credentials": {
"gmailOAuth2": {
"id": "gmail_oauth",
"name": "Gmail OAuth2"
}
}
},
{
"parameters": {
"resource": "message",
"operation": "send",
"chatId": "={{ $vars.SLACK_ONBOARDING_CHANNEL }}",
"text": "🎉 *New Customer Alert!*\n\n👤 **Name:** {{ $json.name }}\n📧 **Email:** {{ $json.email }}\n🏢 **Company:** {{ $json.company || 'Not provided' }}\n📱 **Phone:** {{ $json.phone || 'Not provided' }}\n🌐 **Source:** {{ $json.source || 'Website' }}\n⏰ **Joined:** {{ $now.toLocaleString() }}\n\n🔗 **Actions:**\n• [View in Airtable](https://airtable.com/{{ $vars.AIRTABLE_BASE_ID }}/{{ $node['Create Customer in Airtable'].json.id }})\n• [View in HubSpot](https://app.hubspot.com/contacts/{{ $vars.HUBSPOT_ACCOUNT_ID }}/contact/{{ $node['Create Contact in CRM'].json.id }})\n• [Schedule Onboarding Call]({{ $vars.CALENDLY_LINK }})",
"additionalFields": {
"blocksUi": {
"blocksValues": []
}
}
},
"id": "slack-notification",
"name": "Notify Team in Slack",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.1,
"position": [900, 350],
"credentials": {
"slackOAuth2Api": {
"id": "slack_oauth",
"name": "Slack OAuth2"
}
}
},
{
"parameters": {
"rule": {
"interval": [
{
"field": "days",
"daysInterval": 1
}
]
},
"triggerAtStartup": false
},
"id": "follow-up-trigger",
"name": "24h Follow-up Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [900, 500]
},
{
"parameters": {
"operation": "send",
"resource": "email",
"to": "={{ $json.email }}",
"subject": "How are you settling in? 🤝",
"message": "<!DOCTYPE html><html><head><style>body{font-family:Arial,sans-serif;line-height:1.6;color:#333}.container{max-width:600px;margin:0 auto;padding:20px}.header{background:#28a745;color:white;padding:20px;text-align:center}.content{padding:20px}.button{background:#28a745;color:white;padding:12px 24px;text-decoration:none;border-radius:5px;display:inline-block;margin:10px 0}</style></head><body><div class='container'><div class='header'><h1>How's your experience so far?</h1></div><div class='content'><p>Hi {{ $json.name.split(' ')[0] }},</p><p>It's been 24 hours since you joined {{ $vars.COMPANY_NAME }}, and we wanted to check in!</p><p>Have you had a chance to:</p><ul><li>✅ Explore your dashboard?</li><li>✅ Complete your profile setup?</li><li>✅ Check out our resources?</li></ul><p>If you need any help getting started, don't hesitate to reach out. We're here to help!</p><p><a href='{{ $vars.SUPPORT_URL }}' class='button'>Get Help</a> <a href='{{ $vars.FEEDBACK_URL }}' class='button'>Share Feedback</a></p><p>Best regards,<br>{{ $vars.COMPANY_NAME }} Support Team</p></div></div></body></html>",
"options": {
"replyTo": "support@{{ $vars.COMPANY_DOMAIN }}"
}
},
"id": "follow-up-email",
"name": "Send 24h Follow-up Email",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [1120, 500],
"credentials": {
"gmailOAuth2": {
"id": "gmail_oauth",
"name": "Gmail OAuth2"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": {
"success": true,
"message": "Customer onboarding initiated successfully",
"data": {
"customerId": "={{ $node['Create Customer in Airtable'].json.id }}",
"crmContactId": "={{ $node['Create Contact in CRM'].json.id }}",
"intercomContactId": "={{ $node['Create Contact in Intercom'].json.id }}",
"status": "onboarding_started",
"nextSteps": [
"Welcome email sent",
"Team notified",
"24h follow-up scheduled"
]
}
}
},
"id": "success-response",
"name": "Success Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [1120, 300]
},
{
"parameters": {
"respondWith": "json",
"responseBody": {
"success": false,
"message": "Invalid customer data provided",
"errors": [
"={{ $json.email ? '' : 'Email is required' }}",
"={{ $json.name ? '' : 'Name is required' }}"
].filter(error => error !== '')
}
},
"id": "error-response",
"name": "Error Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [460, 500]
}
],
"connections": {
"New Customer Webhook": {
"main": [
[
{
"node": "Validate Customer Data",
"type": "main",
"index": 0
}
]
]
},
"Validate Customer Data": {
"main": [
[
{
"node": "Create Customer in Airtable",
"type": "main",
"index": 0
},
{
"node": "Create Contact in CRM",
"type": "main",
"index": 0
},
{
"node": "Create Contact in Intercom",
"type": "main",
"index": 0
}
],
[
{
"node": "Error Response",
"type": "main",
"index": 0
}
]
]
},
"Create Customer in Airtable": {
"main": [
[
{
"node": "Send Welcome Email",
"type": "main",
"index": 0
}
]
]
},
"Create Contact in CRM": {
"main": [
[
{
"node": "Notify Team in Slack",
"type": "main",
"index": 0
}
]
]
},
"Send Welcome Email": {
"main": [
[
{
"node": "Success Response",
"type": "main",
"index": 0
}
]
]
},
"24h Follow-up Trigger": {
"main": [
[
{
"node": "Send 24h Follow-up Email",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [
{
"createdAt": "2024-01-15T10:00:00.000Z",
"updatedAt": "2024-01-15T10:00:00.000Z",
"id": "customer-onboarding",
"name": "Customer Onboarding"
}
],
"triggerCount": 0,
"updatedAt": "2024-01-15T10:00:00.000Z",
"versionId": "1"
}
workflows/customer-onboarding.json
Step 3: E-commerce Order Processing Workflow
Create an automated order processing system:
{
"name": "E-commerce Order Processing",
"nodes": [
{
"parameters": {
"authentication": "webhook",
"httpMethod": "POST",
"path": "shopify-webhook",
"responseMode": "responseNode",
"options": {
"noResponseBody": false
}
},
"id": "shopify-webhook",
"name": "Shopify Order Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1,
"position": [240, 300],
"webhookId": "shopify-order-webhook"
},
{
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict"
},
"conditions": [
{
"leftValue": "={{ $json.financial_status }}",
"rightValue": "paid",
"operator": {
"type": "string",
"operation": "equals"
}
}
],
"combinator": "and"
}
},
"id": "check-payment-status",
"name": "Check Payment Status",
"type": "n8n-nodes-base.if",
"typeVersion": 2,
"position": [460, 300]
},
{
"parameters": {
"method": "POST",
"url": "https://api.stripe.com/v1/payment_intents",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "stripeApi",
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"body": "amount={{ Math.round($json.total_price * 100) }}¤cy={{ $json.currency }}&payment_method={{ $json.gateway }}&confirmation_method=manual&confirm=true&metadata[order_id]={{ $json.order_number }}&metadata[customer_email]={{ $json.email }}"
},
"id": "process-payment",
"name": "Process Payment with Stripe",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [680, 200],
"credentials": {
"stripeApi": {
"id": "stripe_api_key",
"name": "Stripe API"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://api.airtable.com/v0/{{ $vars.AIRTABLE_INVENTORY_BASE }}/Inventory",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "airtableTokenApi",
"headers": {
"Content-Type": "application/json"
},
"body": {
"records": "={{ $json.line_items.map(item => ({\n fields: {\n 'Product ID': item.product_id,\n 'Variant ID': item.variant_id,\n 'SKU': item.sku,\n 'Quantity Ordered': item.quantity,\n 'Order Number': $json.order_number,\n 'Status': 'Reserved'\n }\n})) }}"
}
},
"id": "update-inventory",
"name": "Update Inventory in Airtable",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [680, 350],
"credentials": {
"airtableTokenApi": {
"id": "airtable_api_key",
"name": "Airtable API"
}
}
},
{
"parameters": {
"method": "POST",
"url": "https://api.shipstation.com/orders/createorder",
"authentication": "predefinedCredentialType",
"nodeCredentialType": "httpBasicAuth",
"headers": {
"Content-Type": "application/json"
},
"body": {
"orderNumber": "={{ $json.order_number }}",
"orderDate": "={{ $json.created_at }}",
"orderStatus": "awaiting_shipment",
"customerEmail": "={{ $json.email }}",
"billTo": {
"name": "={{ $json.billing_address.name }}",
"company": "={{ $json.billing_address.company }}",
"street1": "={{ $json.billing_address.address1 }}",
"street2": "={{ $json.billing_address.address2 }}",
"city": "={{ $json.billing_address.city }}",
"state": "={{ $json.billing_address.province }}",
"postalCode": "={{ $json.billing_address.zip }}",
"country": "={{ $json.billing_address.country }}",
"phone": "={{ $json.billing_address.phone }}"
},
"shipTo": {
"name": "={{ $json.shipping_address.name }}",
"company": "={{ $json.shipping_address.company }}",
"street1": "={{ $json.shipping_address.address1 }}",
"street2": "={{ $json.shipping_address.address2 }}",
"city": "={{ $json.shipping_address.city }}",
"state": "={{ $json.shipping_address.province }}",
"postalCode": "={{ $json.shipping_address.zip }}",
"country": "={{ $json.shipping_address.country }}",
"phone": "={{ $json.shipping_address.phone }}"
},
"items": "={{ $json.line_items.map(item => ({\n sku: item.sku,\n name: item.name,\n quantity: item.quantity,\n unitPrice: item.price\n })) }}"
}
},
"id": "create-shipment",
"name": "Create Shipment in ShipStation",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.1,
"position": [900, 200],
"credentials": {
"httpBasicAuth": {
"id": "shipstation_auth",
"name": "ShipStation Basic Auth"
}
}
},
{
"parameters": {
"operation": "send",
"resource": "email",
"to": "={{ $json.email }}",
"subject": "Order Confirmation #{{ $json.order_number }} 📦",
"message": "<!DOCTYPE html><html><head><style>body{font-family:Arial,sans-serif;line-height:1.6;color:#333}.container{max-width:600px;margin:0 auto;padding:20px}.header{background:#007bff;color:white;padding:20px;text-align:center}.content{padding:20px}.order-summary{background:#f8f9fa;padding:20px;border-radius:5px;margin:20px 0}.item{display:flex;justify-content:space-between;padding:10px 0;border-bottom:1px solid #dee2e6}.total{font-weight:bold;font-size:1.2em}</style></head><body><div class='container'><div class='header'><h1>Order Confirmed!</h1><p>Thank you for your purchase</p></div><div class='content'><p>Hi {{ $json.customer.first_name }},</p><p>Your order has been confirmed and is being prepared for shipment.</p><div class='order-summary'><h3>Order #{{ $json.order_number }}</h3><p><strong>Order Date:</strong> {{ new Date($json.created_at).toLocaleDateString() }}</p>{{ $json.line_items.map(item => `<div class='item'><span>${item.name} (x${item.quantity})</span><span>$${item.price}</span></div>`).join('') }}<div class='item total'><span>Total</span><span>${{ $json.currency }} {{ $json.total_price }}</span></div></div><p><strong>Shipping Address:</strong><br>{{ $json.shipping_address.address1 }}<br>{{ $json.shipping_address.city }}, {{ $json.shipping_address.province }} {{ $json.shipping_address.zip }}</p><p>You'll receive a shipping confirmation email with tracking information once your order ships.</p><p>Thank you for your business!</p></div></div></body></html>",
"options": {
"replyTo": "orders@{{ $vars.COMPANY_DOMAIN }}"
}
},
"id": "send-confirmation-email",
"name": "Send Order Confirmation",
"type": "n8n-nodes-base.gmail",
"typeVersion": 2,
"position": [900, 350],
"credentials": {
"gmailOAuth2": {
"id": "gmail_oauth",
"name": "Gmail OAuth2"
}
}
},
{
"parameters": {
"resource": "message",
"operation": "send",
"chatId": "={{ $vars.SLACK_ORDERS_CHANNEL }}",
"text": "🛒 *New Order Received!*\n\n📋 **Order:** #{{ $json.order_number }}\n💰 **Total:** {{ $json.currency }} {{ $json.total_price }}\n👤 **Customer:** {{ $json.customer.first_name }} {{ $json.customer.last_name }}\n📧 **Email:** {{ $json.email }}\n📍 **Ship to:** {{ $json.shipping_address.city }}, {{ $json.shipping_address.province }}\n\n📦 **Items:**\n{{ $json.line_items.map(item => `• ${item.name} (x${item.quantity}) - $${item.price}`).join('\n') }}\n\n✅ **Status:** Payment confirmed, ready for fulfillment",
"additionalFields": {}
},
"id": "notify-team",
"name": "Notify Team in Slack",
"type": "n8n-nodes-base.slack",
"typeVersion": 2.1,
"position": [900, 500],
"credentials": {
"slackOAuth2Api": {
"id": "slack_oauth",
"name": "Slack OAuth2"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": {
"success": true,
"message": "Order processed successfully",
"orderId": "={{ $json.order_number }}",
"status": "confirmed"
}
},
"id": "success-response",
"name": "Success Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [1120, 350]
}
],
"connections": {
"Shopify Order Webhook": {
"main": [
[
{
"node": "Check Payment Status",
"type": "main",
"index": 0
}
]
]
},
"Check Payment Status": {
"main": [
[
{
"node": "Process Payment with Stripe",
"type": "main",
"index": 0
},
{
"node": "Update Inventory in Airtable",
"type": "main",
"index": 0
}
]
]
},
"Process Payment with Stripe": {
"main": [
[
{
"node": "Create Shipment in ShipStation",
"type": "main",
"index": 0
}
]
]
},
"Update Inventory in Airtable": {
"main": [
[
{
"node": "Send Order Confirmation",
"type": "main",
"index": 0
}
]
]
},
"Create Shipment in ShipStation": {
"main": [
[
{
"node": "Success Response",
"type": "main",
"index": 0
}
]
]
},
"Send Order Confirmation": {
"main": [
[
{
"node": "Notify Team in Slack",
"type": "main",
"index": 0
}
]
]
},
"Notify Team in Slack": {
"main": [
[
{
"node": "Success Response",
"type": "main",
"index": 0
}
]
]
}
}
}
workflows/ecommerce-order-processing.json
Step 4: Custom Node Development
Create a custom node for advanced functionality:
import {
IExecuteFunctions,
INodeExecutionData,
INodeType,
INodeTypeDescription,
ILoadOptionsFunctions,
INodePropertyOptions,
NodeOperationError,
} from "n8n-workflow";
export class WebtrophyNode implements INodeType {
description: INodeTypeDescription = {
displayName: "Webtrophy",
name: "webtrophy",
icon: "file:webtrophy.svg",
group: ["transform"],
version: 1,
description: "Custom Webtrophy business operations",
defaults: {
name: "Webtrophy",
},
inputs: ["main"],
outputs: ["main"],
credentials: [
{
name: "webtrophyApi",
required: false,
},
],
properties: [
{
displayName: "Resource",
name: "resource",
type: "options",
noDataExpression: true,
options: [
{
name: "Client",
value: "client",
},
{
name: "Project",
value: "project",
},
{
name: "Invoice",
value: "invoice",
},
{
name: "Report",
value: "report",
},
],
default: "client",
},
{
displayName: "Operation",
name: "operation",
type: "options",
noDataExpression: true,
displayOptions: {
show: {
resource: ["client"],
},
},
options: [
{
name: "Create",
value: "create",
description: "Create a new client",
action: "Create a client",
},
{
name: "Get",
value: "get",
description: "Get a client",
action: "Get a client",
},
{
name: "Update",
value: "update",
description: "Update a client",
action: "Update a client",
},
{
name: "Get All",
value: "getAll",
description: "Get all clients",
action: "Get all clients",
},
],
default: "create",
},
{
displayName: "Client Name",
name: "clientName",
type: "string",
required: true,
displayOptions: {
show: {
resource: ["client"],
operation: ["create", "update"],
},
},
default: "",
description: "Name of the client",
},
{
displayName: "Email",
name: "email",
type: "string",
placeholder: "name@email.com",
required: true,
displayOptions: {
show: {
resource: ["client"],
operation: ["create", "update"],
},
},
default: "",
description: "Email address of the client",
},
{
displayName: "Company",
name: "company",
type: "string",
displayOptions: {
show: {
resource: ["client"],
operation: ["create", "update"],
},
},
default: "",
description: "Company name",
},
{
displayName: "Phone",
name: "phone",
type: "string",
displayOptions: {
show: {
resource: ["client"],
operation: ["create", "update"],
},
},
default: "",
description: "Phone number",
},
{
displayName: "Client ID",
name: "clientId",
type: "string",
required: true,
displayOptions: {
show: {
resource: ["client"],
operation: ["get", "update"],
},
},
default: "",
description: "ID of the client",
},
{
displayName: "Limit",
name: "limit",
type: "number",
displayOptions: {
show: {
resource: ["client"],
operation: ["getAll"],
},
},
typeOptions: {
minValue: 1,
maxValue: 100,
},
default: 20,
description: "Maximum number of results to return",
},
{
displayName: "Additional Fields",
name: "additionalFields",
type: "collection",
placeholder: "Add Field",
displayOptions: {
show: {
resource: ["client"],
operation: ["create", "update"],
},
},
default: {},
options: [
{
displayName: "Address",
name: "address",
type: "string",
default: "",
},
{
displayName: "Notes",
name: "notes",
type: "string",
typeOptions: {
rows: 4,
},
default: "",
},
{
displayName: "Tags",
name: "tags",
type: "string",
default: "",
description: "Comma-separated list of tags",
},
],
},
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: INodeExecutionData[] = [];
const resource = this.getNodeParameter("resource", 0) as string;
const operation = this.getNodeParameter("operation", 0) as string;
for (let i = 0; i < items.length; i++) {
try {
if (resource === "client") {
if (operation === "create") {
const clientName = this.getNodeParameter("clientName", i) as string;
const email = this.getNodeParameter("email", i) as string;
const company = this.getNodeParameter("company", i) as string;
const phone = this.getNodeParameter("phone", i) as string;
const additionalFields = this.getNodeParameter(
"additionalFields",
i
) as any;
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new NodeOperationError(
this.getNode(),
"Invalid email format"
);
}
// Create client data
const clientData = {
id: generateClientId(),
name: clientName,
email,
company,
phone,
address: additionalFields.address || "",
notes: additionalFields.notes || "",
tags: additionalFields.tags
? additionalFields.tags
.split(",")
.map((tag: string) => tag.trim())
: [],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
status: "active",
projects: [],
totalBilled: 0,
};
// Simulate API call to create client
const response = await this.createClient(clientData);
returnData.push({
json: {
success: true,
client: response,
message: "Client created successfully",
},
});
}
if (operation === "get") {
const clientId = this.getNodeParameter("clientId", i) as string;
// Simulate API call to get client
const client = await this.getClient(clientId);
if (!client) {
throw new NodeOperationError(
this.getNode(),
`Client with ID ${clientId} not found`
);
}
returnData.push({
json: client,
});
}
if (operation === "getAll") {
const limit = this.getNodeParameter("limit", i) as number;
// Simulate API call to get all clients
const clients = await this.getAllClients(limit);
returnData.push({
json: {
clients,
total: clients.length,
limit,
},
});
}
if (operation === "update") {
const clientId = this.getNodeParameter("clientId", i) as string;
const clientName = this.getNodeParameter("clientName", i) as string;
const email = this.getNodeParameter("email", i) as string;
const company = this.getNodeParameter("company", i) as string;
const phone = this.getNodeParameter("phone", i) as string;
const additionalFields = this.getNodeParameter(
"additionalFields",
i
) as any;
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
throw new NodeOperationError(
this.getNode(),
"Invalid email format"
);
}
// Update client data
const updateData = {
name: clientName,
email,
company,
phone,
address: additionalFields.address || "",
notes: additionalFields.notes || "",
tags: additionalFields.tags
? additionalFields.tags
.split(",")
.map((tag: string) => tag.trim())
: [],
updatedAt: new Date().toISOString(),
};
// Simulate API call to update client
const updatedClient = await this.updateClient(clientId, updateData);
if (!updatedClient) {
throw new NodeOperationError(
this.getNode(),
`Client with ID ${clientId} not found`
);
}
returnData.push({
json: {
success: true,
client: updatedClient,
message: "Client updated successfully",
},
});
}
}
} catch (error) {
if (this.continueOnFail()) {
returnData.push({
json: {
error: error.message,
},
});
continue;
}
throw error;
}
}
return [returnData];
}
// Helper methods to simulate API calls
private async createClient(clientData: any): Promise<any> {
// In a real implementation, this would make an HTTP request to your API
console.log("Creating client:", clientData);
return clientData;
}
private async getClient(clientId: string): Promise<any> {
// In a real implementation, this would make an HTTP request to your API
console.log("Getting client:", clientId);
return {
id: clientId,
name: "Sample Client",
email: "client@example.com",
company: "Sample Company",
phone: "+1234567890",
address: "123 Main St",
notes: "Sample notes",
tags: ["web-development", "priority"],
createdAt: "2024-01-01T00:00:00.000Z",
updatedAt: "2024-01-15T00:00:00.000Z",
status: "active",
projects: [],
totalBilled: 5000,
};
}
private async getAllClients(limit: number): Promise<any[]> {
// In a real implementation, this would make an HTTP request to your API
console.log("Getting all clients, limit:", limit);
return [
{
id: "client-1",
name: "Client One",
email: "client1@example.com",
company: "Company One",
},
{
id: "client-2",
name: "Client Two",
email: "client2@example.com",
company: "Company Two",
},
].slice(0, limit);
}
private async updateClient(clientId: string, updateData: any): Promise<any> {
// In a real implementation, this would make an HTTP request to your API
console.log("Updating client:", clientId, updateData);
return {
id: clientId,
...updateData,
};
}
}
// Helper function to generate client ID
function generateClientId(): string {
return "client_" + Date.now() + "_" + Math.random().toString(36).substr(2, 9);
}
custom-nodes/WebtrophyNode/WebtrophyNode.node.ts
Step 5: Monitoring and Analytics Dashboard
Create monitoring workflows and scripts:
#!/bin/bash
# n8n Monitoring and Analytics Script
N8N_URL="http://localhost:5678"
API_KEY="${N8N_API_KEY}"
WEBHOOK_URL="${SLACK_WEBHOOK_URL}"
LOG_FILE="/var/log/n8n-monitor.log"
# Function to log messages
log() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
# Function to send Slack notification
send_slack_notification() {
local message="$1"
local color="$2"
curl -X POST -H 'Content-type: application/json' \
--data "{
\"attachments\": [{
\"color\": \"$color\",
\"title\": \"n8n Monitor Alert\",
\"text\": \"$message\",
\"footer\": \"n8n Monitor\",
\"ts\": $(date +%s)
}]
}" \
"$WEBHOOK_URL"
}
# Check n8n health
check_n8n_health() {
log "Checking n8n health..."
local health_response=$(curl -s -o /dev/null -w "%{http_code}" "$N8N_URL/healthz")
if [ "$health_response" = "200" ]; then
log "✅ n8n is healthy"
return 0
else
log "❌ n8n health check failed (HTTP $health_response)"
send_slack_notification "🚨 n8n instance is not responding (HTTP $health_response)" "danger"
return 1
fi
}
# Check workflow executions
check_workflow_executions() {
log "Checking recent workflow executions..."
# Get executions from last hour
local executions=$(curl -s -H "Authorization: Bearer $API_KEY" \
"$N8N_URL/api/v1/executions?limit=100&includeData=false")
if [ $? -eq 0 ]; then
# Count failed executions
local failed_count=$(echo "$executions" | jq '[.data[] | select(.finished == false and .stoppedAt != null)] | length')
if [ "$failed_count" -gt 5 ]; then
log "⚠️ High number of failed executions: $failed_count"
send_slack_notification "⚠️ High number of failed workflow executions: $failed_count" "warning"
else
log "✅ Workflow execution health: $failed_count failed executions"
fi
else
log "❌ Failed to fetch execution data"
send_slack_notification "🚨 Unable to fetch n8n execution data" "danger"
fi
}
# Check database connectivity
check_database() {
log "Checking database connectivity..."
if docker exec n8n-postgres pg_isready -U n8n -d n8n > /dev/null 2>&1; then
log "✅ Database is healthy"
else
log "❌ Database connectivity issues"
send_slack_notification "🚨 n8n database connectivity issues detected" "danger"
fi
}
# Check disk space
check_disk_space() {
log "Checking disk space..."
local usage=$(df /var/lib/docker | tail -1 | awk '{print $5}' | sed 's/%//')
if [ "$usage" -gt 85 ]; then
log "⚠️ High disk usage: ${usage}%"
send_slack_notification "⚠️ High disk usage on n8n server: ${usage}%" "warning"
else
log "✅ Disk usage: ${usage}%"
fi
}
# Get workflow statistics
get_workflow_stats() {
log "Gathering workflow statistics..."
local workflows=$(curl -s -H "Authorization: Bearer $API_KEY" \
"$N8N_URL/api/v1/workflows")
if [ $? -eq 0 ]; then
local total_workflows=$(echo "$workflows" | jq '.data | length')
local active_workflows=$(echo "$workflows" | jq '[.data[] | select(.active == true)] | length')
log "📊 Workflow Statistics:"
log " Total workflows: $total_workflows"
log " Active workflows: $active_workflows"
# Send daily summary (only at 9 AM)
if [ "$(date +%H)" = "09" ]; then
send_slack_notification "📊 Daily n8n Summary:\n• Total workflows: $total_workflows\n• Active workflows: $active_workflows" "good"
fi
fi
}
# Main monitoring function
main() {
log "Starting n8n monitoring check..."
check_n8n_health
check_workflow_executions
check_database
check_disk_space
get_workflow_stats
log "Monitoring check completed"
}
# Run monitoring
main
scripts/n8n-monitor.sh
Best Practices Summary
- Use meaningful workflow names and add descriptions
- Implement error handling in all workflow branches
- Set up monitoring and alerting for critical workflows
- Use environment variables for sensitive data
- Create reusable sub-workflows for common operations
- Test workflows thoroughly before production deployment
- Document your workflows with clear node names
- Implement proper logging for debugging
- Use version control for workflow JSON exports
- Monitor resource usage and optimize performance
Management Commands
# Start n8n services
docker-compose up -d
# View n8n logs
docker-compose logs -f n8n
# Export workflows
n8n export:workflow --backup --output=./backups/
# Import workflows
n8n import:workflow --input=./workflows/
# Check n8n status
curl http://localhost:5678/healthz
# Access n8n CLI
docker exec -it n8n n8n --help
Your n8n workflow automation platform is now ready for production with comprehensive business process automation, monitoring, and scalable integrations!