n8n Automation

n8n AI Workflow Integration: Building Intelligent Automation with LLMs

DeviDevs Team
11 min read
#n8n#AI integration#LLM#workflow automation#OpenAI

AI integration transforms n8n workflows from rule-based automation to intelligent decision-making systems. This guide covers practical AI workflow patterns using LLMs for document processing, content generation, and smart routing.

AI Node Configuration

OpenAI Integration Setup

// OpenAI Configuration for n8n
// Store credentials securely in n8n
{
  "openAiApi": {
    "apiKey": "{{ $env.OPENAI_API_KEY }}",
    "organization": "{{ $env.OPENAI_ORG_ID }}"
  }
}
 
// Function Node: OpenAI API Helper
const OPENAI_API_URL = 'https://api.openai.com/v1';
 
async function callOpenAI(endpoint, payload) {
  const response = await fetch(`${OPENAI_API_URL}${endpoint}`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${$env.OPENAI_API_KEY}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
 
  if (!response.ok) {
    throw new Error(`OpenAI API error: ${response.status}`);
  }
 
  return response.json();
}
 
// Export for use in other nodes
return [{
  json: {
    callOpenAI: callOpenAI.toString()
  }
}];

Claude/Anthropic Integration

// Function Node: Anthropic Claude Integration
const ANTHROPIC_API_URL = 'https://api.anthropic.com/v1/messages';
 
async function callClaude(systemPrompt, userMessage, options = {}) {
  const payload = {
    model: options.model || 'claude-3-sonnet-20240229',
    max_tokens: options.maxTokens || 1024,
    system: systemPrompt,
    messages: [
      { role: 'user', content: userMessage }
    ]
  };
 
  const response = await fetch(ANTHROPIC_API_URL, {
    method: 'POST',
    headers: {
      'x-api-key': $env.ANTHROPIC_API_KEY,
      'anthropic-version': '2023-06-01',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
 
  if (!response.ok) {
    const error = await response.text();
    throw new Error(`Claude API error: ${error}`);
  }
 
  const data = await response.json();
  return data.content[0].text;
}
 
const input = $input.first().json;
 
const result = await callClaude(
  input.systemPrompt,
  input.userMessage,
  { maxTokens: input.maxTokens || 1024 }
);
 
return [{
  json: {
    response: result,
    model: 'claude-3-sonnet',
    timestamp: new Date().toISOString()
  }
}];

Intelligent Document Processing

Document Classification

// Function Node: AI Document Classifier
const document = $input.first().json;
 
const systemPrompt = `You are a document classification expert. Classify documents into one of these categories:
- invoice: Bills, invoices, payment requests
- contract: Legal agreements, terms of service
- resume: CVs, job applications
- correspondence: Letters, emails, memos
- report: Analysis, research, summaries
- receipt: Purchase receipts, transaction records
- form: Applications, registrations
- other: Anything that doesn't fit above
 
Respond with ONLY a JSON object containing:
- category: The classification category
- confidence: A number from 0 to 1 indicating confidence
- reasoning: Brief explanation of classification`;
 
const userMessage = `Classify this document:
 
Title: ${document.title || 'Untitled'}
Content Preview:
${document.content.substring(0, 2000)}`;
 
const response = await callClaude(systemPrompt, userMessage);
 
// Parse JSON response
let classification;
try {
  classification = JSON.parse(response);
} catch {
  // Handle non-JSON response
  classification = {
    category: 'other',
    confidence: 0.5,
    reasoning: response
  };
}
 
return [{
  json: {
    ...document,
    classification: {
      ...classification,
      classifiedAt: new Date().toISOString()
    }
  }
}];

Invoice Data Extraction

// Function Node: AI Invoice Data Extractor
const invoiceText = $input.first().json.content;
 
const systemPrompt = `You are an expert at extracting structured data from invoices. Extract the following information and return as JSON:
 
Required fields:
- vendor_name: Company issuing the invoice
- invoice_number: Invoice/bill number
- invoice_date: Date of invoice (ISO format YYYY-MM-DD)
- due_date: Payment due date (ISO format)
- total_amount: Total amount due (number only)
- currency: Currency code (USD, EUR, etc.)
 
Optional fields:
- line_items: Array of {description, quantity, unit_price, total}
- subtotal: Subtotal before tax
- tax_amount: Tax amount
- tax_rate: Tax percentage
- payment_terms: Net 30, etc.
- po_number: Purchase order reference
- vendor_address: Full address
- billing_address: Bill to address
 
If a field cannot be determined, use null.`;
 
const response = await callClaude(systemPrompt, `Extract data from this invoice:\n\n${invoiceText}`);
 
let extractedData;
try {
  extractedData = JSON.parse(response);
} catch {
  throw new Error('Failed to parse invoice extraction response');
}
 
// Validate required fields
const requiredFields = ['vendor_name', 'invoice_number', 'total_amount'];
const missingFields = requiredFields.filter(f => !extractedData[f]);
 
if (missingFields.length > 0) {
  extractedData._validation = {
    valid: false,
    missingFields
  };
} else {
  extractedData._validation = { valid: true };
}
 
return [{
  json: {
    original: invoiceText,
    extracted: extractedData,
    extractedAt: new Date().toISOString()
  }
}];

Automated Content Generation

Blog Post Generator

// Function Node: AI Blog Post Generator
const topic = $input.first().json;
 
const systemPrompt = `You are an expert content writer specializing in ${topic.industry || 'technology'}.
Write engaging, informative blog posts that are:
- SEO-optimized with natural keyword placement
- Well-structured with clear headings
- Actionable with practical takeaways
- Between 1000-1500 words
 
Format the output as JSON with:
- title: Compelling, SEO-friendly title
- meta_description: 150-160 character description
- slug: URL-friendly slug
- content: Full markdown content
- tags: Array of relevant tags
- estimated_reading_time: Minutes to read`;
 
const userMessage = `Write a blog post about: ${topic.title}
 
Target keywords: ${topic.keywords?.join(', ') || 'none specified'}
Target audience: ${topic.audience || 'general'}
Tone: ${topic.tone || 'professional'}
 
Additional context: ${topic.context || 'none'}`;
 
const response = await callOpenAI('/chat/completions', {
  model: 'gpt-4-turbo-preview',
  messages: [
    { role: 'system', content: systemPrompt },
    { role: 'user', content: userMessage }
  ],
  response_format: { type: 'json_object' },
  max_tokens: 4000
});
 
const blogPost = JSON.parse(response.choices[0].message.content);
 
return [{
  json: {
    ...blogPost,
    generatedAt: new Date().toISOString(),
    model: 'gpt-4-turbo-preview',
    topic: topic
  }
}];

Email Response Generator

// Function Node: AI Email Response Generator
const email = $input.first().json;
 
const systemPrompt = `You are a professional customer service representative.
Generate helpful, empathetic email responses that:
- Address all points in the customer's email
- Use a warm but professional tone
- Provide clear next steps when applicable
- Keep responses concise (under 200 words)
 
Respond with JSON containing:
- subject: Reply subject line
- body: Email body (plain text)
- sentiment: detected sentiment of original (positive/neutral/negative)
- urgency: low/medium/high
- suggested_actions: Array of internal actions to take`;
 
const userMessage = `Generate a response to this customer email:
 
From: ${email.from}
Subject: ${email.subject}
Body:
${email.body}
 
Customer history:
- Account age: ${email.customerData?.accountAge || 'unknown'}
- Previous tickets: ${email.customerData?.ticketCount || 0}
- VIP status: ${email.customerData?.isVip || false}`;
 
const response = await callClaude(systemPrompt, userMessage);
 
const emailResponse = JSON.parse(response);
 
return [{
  json: {
    originalEmail: email,
    response: emailResponse,
    generatedAt: new Date().toISOString()
  }
}];

Sentiment Analysis and Routing

Customer Feedback Analyzer

// Function Node: Sentiment Analysis and Routing
const feedback = $input.first().json;
 
const systemPrompt = `Analyze customer feedback and provide:
1. Sentiment score (-1 to 1, where -1 is very negative, 1 is very positive)
2. Emotion categories present (joy, anger, frustration, satisfaction, confusion)
3. Key topics mentioned
4. Urgency level (1-5)
5. Suggested department routing
6. Recommended priority
 
Return as JSON with these exact fields:
- sentiment_score: number
- sentiment_label: "very_negative" | "negative" | "neutral" | "positive" | "very_positive"
- emotions: string[]
- topics: string[]
- urgency: number
- route_to: "support" | "sales" | "billing" | "technical" | "management"
- priority: "low" | "normal" | "high" | "urgent"
- requires_human: boolean
- summary: string (one sentence)`;
 
const response = await callClaude(systemPrompt, `Analyze this feedback:\n\n${feedback.text}`);
 
const analysis = JSON.parse(response);
 
// Enrich with routing logic
const routingRules = {
  escalate_to_management: analysis.sentiment_score < -0.7 && analysis.urgency >= 4,
  auto_respond: analysis.sentiment_score > 0.5 && !analysis.requires_human,
  priority_queue: analysis.urgency >= 4 || analysis.priority === 'urgent'
};
 
return [{
  json: {
    original: feedback,
    analysis,
    routing: routingRules,
    analyzedAt: new Date().toISOString()
  }
}];

Smart Ticket Routing

// Function Node: AI-Powered Ticket Router
const ticket = $input.first().json;
 
const systemPrompt = `You are an expert at routing support tickets to the right team.
 
Available teams and their expertise:
- technical_support: Software bugs, errors, technical issues, API problems
- billing: Payments, invoices, refunds, subscription issues
- sales: Pricing questions, upgrades, enterprise inquiries
- onboarding: New user setup, getting started, tutorials
- account: Password resets, access issues, profile changes
- product: Feature requests, feedback, suggestions
- security: Data concerns, privacy, security incidents
 
Analyze the ticket and provide:
- primary_team: Main team to handle
- secondary_team: Backup team if needed
- confidence: 0-1 confidence in routing
- skills_required: Specific skills needed
- estimated_complexity: 1-5
- auto_response_possible: boolean
- suggested_response: Brief initial response if auto_response_possible`;
 
const ticketContext = `
Subject: ${ticket.subject}
Description: ${ticket.description}
Customer tier: ${ticket.customerTier || 'standard'}
Previous category: ${ticket.previousCategory || 'none'}
`;
 
const response = await callClaude(systemPrompt, ticketContext);
const routing = JSON.parse(response);
 
// Apply business rules
if (ticket.customerTier === 'enterprise') {
  routing.priority_boost = true;
  routing.sla_hours = 4;
} else if (ticket.customerTier === 'premium') {
  routing.sla_hours = 24;
} else {
  routing.sla_hours = 48;
}
 
return [{
  json: {
    ticket,
    routing,
    routedAt: new Date().toISOString()
  }
}];

AI-Enhanced Decision Making

Approval Workflow with AI

// Function Node: AI-Assisted Approval Decision
const request = $input.first().json;
 
const systemPrompt = `You are a risk assessment AI for expense and purchase approvals.
 
Evaluate requests based on:
1. Amount reasonableness for the category
2. Business justification quality
3. Policy compliance
4. Historical patterns (if provided)
5. Budget impact
 
Provide:
- recommendation: "approve" | "review" | "deny"
- confidence: 0-1
- risk_score: 1-10 (10 = highest risk)
- concerns: Array of potential issues
- questions: Questions for the requester if review needed
- policy_references: Relevant policies to check`;
 
const requestDetails = `
Request Type: ${request.type}
Amount: ${request.amount} ${request.currency}
Category: ${request.category}
Requester: ${request.requester.name} (${request.requester.department})
Justification: ${request.justification}
Budget remaining: ${request.budgetRemaining || 'unknown'}
Previous requests this month: ${request.monthlyRequestCount || 0}
Historical average for this category: ${request.categoryAverage || 'unknown'}
`;
 
const response = await callClaude(systemPrompt, requestDetails);
const assessment = JSON.parse(response);
 
// Apply thresholds
let finalDecision;
if (assessment.recommendation === 'approve' && assessment.confidence > 0.8) {
  finalDecision = 'auto_approve';
} else if (assessment.recommendation === 'deny' && assessment.confidence > 0.9) {
  finalDecision = 'auto_deny';
} else {
  finalDecision = 'human_review';
}
 
return [{
  json: {
    request,
    assessment,
    decision: finalDecision,
    assessedAt: new Date().toISOString()
  }
}];

RAG (Retrieval-Augmented Generation)

Knowledge Base Search and Response

// Function Node: RAG-Enhanced Support Response
const query = $input.first().json;
 
// Step 1: Search knowledge base (using vector database)
async function searchKnowledgeBase(queryText) {
  // Generate embedding for query
  const embeddingResponse = await callOpenAI('/embeddings', {
    model: 'text-embedding-ada-002',
    input: queryText
  });
 
  const queryEmbedding = embeddingResponse.data[0].embedding;
 
  // Search vector database (e.g., Pinecone, Weaviate)
  const searchResults = await fetch(`${$env.VECTOR_DB_URL}/query`, {
    method: 'POST',
    headers: {
      'Api-Key': $env.VECTOR_DB_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      vector: queryEmbedding,
      topK: 5,
      includeMetadata: true
    })
  });
 
  return searchResults.json();
}
 
// Step 2: Search knowledge base
const searchResults = await searchKnowledgeBase(query.question);
 
// Step 3: Build context from results
const context = searchResults.matches
  .filter(m => m.score > 0.7)
  .map(m => m.metadata.content)
  .join('\n\n---\n\n');
 
// Step 4: Generate response with context
const systemPrompt = `You are a helpful support assistant. Use the provided context to answer questions.
If the context doesn't contain relevant information, say so and provide general guidance.
Always cite which source you're using when possible.`;
 
const userMessage = `Context from knowledge base:
${context}
 
---
 
User question: ${query.question}
 
Provide a helpful, accurate response based on the context above.`;
 
const response = await callClaude(systemPrompt, userMessage);
 
return [{
  json: {
    question: query.question,
    answer: response,
    sources: searchResults.matches.slice(0, 3).map(m => ({
      title: m.metadata.title,
      score: m.score
    })),
    generatedAt: new Date().toISOString()
  }
}];

Error Handling and Fallbacks

AI Workflow Error Handler

// Function Node: AI Error Handler with Fallbacks
const input = $input.first().json;
const error = $input.first().json.error;
 
// Categorize error
let errorType;
let fallbackAction;
 
if (error.message?.includes('rate limit')) {
  errorType = 'rate_limit';
  fallbackAction = 'retry_with_backoff';
} else if (error.message?.includes('context length')) {
  errorType = 'context_too_long';
  fallbackAction = 'truncate_and_retry';
} else if (error.message?.includes('API')) {
  errorType = 'api_error';
  fallbackAction = 'use_fallback_model';
} else {
  errorType = 'unknown';
  fallbackAction = 'human_review';
}
 
// Prepare fallback
let fallbackConfig = null;
 
switch (fallbackAction) {
  case 'retry_with_backoff':
    fallbackConfig = {
      delay: 60000,
      maxRetries: 3,
      currentRetry: (input._retryCount || 0) + 1
    };
    break;
 
  case 'truncate_and_retry':
    fallbackConfig = {
      maxTokens: 2000,
      truncatedContent: input.content?.substring(0, 8000)
    };
    break;
 
  case 'use_fallback_model':
    fallbackConfig = {
      primaryModel: input.model,
      fallbackModel: 'gpt-3.5-turbo',
      reason: 'Primary model unavailable'
    };
    break;
 
  case 'human_review':
    fallbackConfig = {
      assignTo: 'ai-failures-queue',
      originalInput: input
    };
    break;
}
 
return [{
  json: {
    errorType,
    errorMessage: error.message,
    fallbackAction,
    fallbackConfig,
    originalInput: input,
    timestamp: new Date().toISOString()
  }
}];

Best Practices

  1. Use structured outputs: Request JSON responses for reliable parsing
  2. Implement fallbacks: Have backup models and human review paths
  3. Monitor costs: Track token usage and implement limits
  4. Cache responses: Cache common queries to reduce API calls
  5. Handle rate limits: Implement exponential backoff
  6. Validate outputs: Always validate AI responses before using
  7. Log everything: Keep detailed logs for debugging and improvement
  8. Set timeouts: Prevent workflows from hanging on slow API calls

AI integration with n8n enables intelligent automation that goes beyond simple rules. Start with clear use cases and expand as you build confidence in AI-assisted workflows.

Weekly AI Security & Automation Digest

Get the latest on AI Security, workflow automation, secure integrations, and custom platform development delivered weekly.

No spam. Unsubscribe anytime.