n8n Automation

Automatizare suport clienti cu n8n

Petru Constantin
--11 min lectura
#n8n#customer-support#automation#helpdesk#ai-support

Suportul automatizat imbunatateste timpii de raspuns si eficienta agentilor. Acest ghid acopera constructia de workflow-uri inteligente de suport cu n8n pentru rutare tichete, raspunsuri AI si gestionare multi-canal.

Rutare inteligenta a tichetelor

Construieste clasificare si rutare inteligenta a tichetelor:

// Multi-Channel Ticket Ingestion Workflow
 
// 1. Email Ticket Trigger
const emailTrigger = {
  name: "Email Ticket Trigger",
  type: "n8n-nodes-base.imapEmail",
  parameters: {
    mailbox: "INBOX",
    action: "read",
    options: {
      markSeen: true,
      forceReconnect: true
    }
  }
};
 
// 2. Ticket Classification Function
function classifyTicket(items) {
  const ticket = items[0].json;
  const subject = (ticket.subject || '').toLowerCase();
  const body = (ticket.text || ticket.html || '').toLowerCase();
  const content = subject + ' ' + body;
 
  // Clasificare pe categorii
  const categories = {
    billing: ['invoice', 'payment', 'charge', 'refund', 'subscription', 'pricing', 'bill'],
    technical: ['error', 'bug', 'crash', 'not working', 'broken', 'issue', 'problem', 'failed'],
    account: ['password', 'login', 'access', 'account', 'profile', 'settings', 'security'],
    sales: ['demo', 'pricing', 'enterprise', 'quote', 'trial', 'upgrade', 'plan'],
    general: ['question', 'help', 'information', 'inquiry', 'support']
  };
 
  let detectedCategory = 'general';
  let maxScore = 0;
 
  for (const [category, keywords] of Object.entries(categories)) {
    const score = keywords.filter(kw => content.includes(kw)).length;
    if (score > maxScore) {
      maxScore = score;
      detectedCategory = category;
    }
  }
 
  // Detectare prioritate
  const urgentKeywords = ['urgent', 'asap', 'immediately', 'critical', 'emergency', 'down', 'outage'];
  const highKeywords = ['important', 'blocking', 'cannot', 'broken', 'failed'];
 
  let priority = 'normal';
  if (urgentKeywords.some(kw => content.includes(kw))) {
    priority = 'urgent';
  } else if (highKeywords.some(kw => content.includes(kw))) {
    priority = 'high';
  }
 
  // Analiza de sentiment (simplificata)
  const negativeWords = ['frustrated', 'angry', 'disappointed', 'terrible', 'worst', 'hate', 'unacceptable'];
  const sentiment = negativeWords.some(w => content.includes(w)) ? 'negative' : 'neutral';
 
  return [{
    json: {
      ticket_id: `TKT-${Date.now()}`,
      source: 'email',
      from: ticket.from,
      subject: ticket.subject,
      body: ticket.text || ticket.html,
      received_at: new Date().toISOString(),
      category: detectedCategory,
      priority: priority,
      sentiment: sentiment,
      confidence: Math.min(maxScore / 3, 1),
      raw_email: ticket
    }
  }];
}
 
// 3. Rutare catre coada corespunzatoare
const routeTicket = {
  name: "Route Ticket",
  type: "n8n-nodes-base.switch",
  parameters: {
    rules: {
      rules: [
        {
          value1: "={{$json.priority}}",
          operation: "equals",
          value2: "urgent"
        },
        {
          value1: "={{$json.category}}",
          operation: "equals",
          value2: "billing"
        },
        {
          value1: "={{$json.category}}",
          operation: "equals",
          value2: "technical"
        },
        {
          value1: "={{$json.category}}",
          operation: "equals",
          value2: "sales"
        }
      ]
    }
  }
};
 
// 4. Asignare la agent pe baza de competente si disponibilitate
async function assignToAgent(items) {
  const ticket = items[0].json;
 
  // Ia agentii disponibili cu competentele potrivite
  const agents = await getAvailableAgents(ticket.category);
 
  // Load balancing - gaseste agentul cu cel mai mic workload curent
  let selectedAgent = null;
  let lowestWorkload = Infinity;
 
  for (const agent of agents) {
    const workload = await getAgentWorkload(agent.id);
    if (workload < lowestWorkload && workload < agent.max_tickets) {
      lowestWorkload = workload;
      selectedAgent = agent;
    }
  }
 
  // Daca niciun agent nu e disponibil, adauga in coada
  if (!selectedAgent) {
    return [{
      json: {
        ...ticket,
        status: 'queued',
        assigned_agent: null,
        queue_position: await getQueuePosition(ticket.category)
      }
    }];
  }
 
  return [{
    json: {
      ...ticket,
      status: 'assigned',
      assigned_agent: {
        id: selectedAgent.id,
        name: selectedAgent.name,
        email: selectedAgent.email
      },
      assigned_at: new Date().toISOString()
    }
  }];
}
 
// Functii helper
async function getAvailableAgents(category) {
  // Ar interoga baza de date a agentilor
  return [
    { id: 'agent1', name: 'Alice', email: 'alice@company.com', skills: ['billing', 'general'], max_tickets: 10 },
    { id: 'agent2', name: 'Bob', email: 'bob@company.com', skills: ['technical', 'general'], max_tickets: 8 }
  ].filter(a => a.skills.includes(category) || a.skills.includes('general'));
}
 
async function getAgentWorkload(agentId) {
  // Ar interoga asignarile curente de tichete
  return Math.floor(Math.random() * 10);
}
 
async function getQueuePosition(category) {
  return Math.floor(Math.random() * 20) + 1;
}

Generare raspunsuri cu AI

Genereaza raspunsuri inteligente cu AI:

// AI Response Generation Workflow
 
// 1. Pregateste contextul pentru AI
function prepareAIContext(items) {
  const ticket = items[0].json;
 
  // Construieste context din istoricul tichetului si baza de cunostinte
  const context = {
    ticket_id: ticket.ticket_id,
    category: ticket.category,
    customer_message: ticket.body,
    customer_email: ticket.from,
    priority: ticket.priority,
    sentiment: ticket.sentiment
  };
 
  // System prompt pe baza categoriei
  const systemPrompts = {
    billing: `You are a helpful billing support agent. Be empathetic about billing concerns.
              Focus on resolving payment issues quickly. Always offer to help with refunds or adjustments when appropriate.`,
    technical: `You are a technical support specialist. Ask clarifying questions about the issue.
                Provide step-by-step troubleshooting instructions. Escalate complex issues appropriately.`,
    account: `You are an account security specialist. Verify identity carefully before making changes.
              Guide users through secure password reset procedures.`,
    sales: `You are a sales representative. Be enthusiastic but not pushy.
            Focus on understanding customer needs and matching them with appropriate solutions.`,
    general: `You are a friendly customer support agent. Be helpful and professional.
              If you can't answer something, offer to connect the customer with the right team.`
  };
 
  context.system_prompt = systemPrompts[ticket.category] || systemPrompts.general;
 
  return [{ json: context }];
}
 
// 2. Genereaza raspuns AI
const generateAIResponse = {
  name: "Generate AI Response",
  type: "n8n-nodes-base.openAi",
  parameters: {
    operation: "text",
    model: "gpt-4",
    messages: [
      {
        role: "system",
        content: `{{$json.system_prompt}}
 
Your response guidelines:
- Be professional and empathetic
- Keep responses concise but complete
- Use proper grammar and formatting
- Include specific next steps when applicable
- Never share sensitive information
- If unsure, offer to escalate to a human agent`
      },
      {
        role: "user",
        content: `Customer email: {{$json.customer_email}}
Category: {{$json.category}}
Priority: {{$json.priority}}
Customer sentiment: {{$json.sentiment}}
 
Customer message:
{{$json.customer_message}}
 
Please draft a helpful response.`
      }
    ],
    options: {
      temperature: 0.7,
      maxTokens: 500
    }
  }
};
 
// 3. Verificare calitate raspuns
function checkResponseQuality(items) {
  const aiResponse = items[0].json;
  const context = $('Prepare AI Context').first().json;
  const responseText = aiResponse.message?.content || aiResponse.text || '';
 
  const qualityChecks = {
    hasGreeting: /^(hi|hello|dear|thank)/i.test(responseText),
    hasSignoff: /(regards|sincerely|best|thanks)/i.test(responseText),
    appropriateLength: responseText.length >= 50 && responseText.length <= 2000,
    noSensitiveInfo: !/(password|credit card|ssn|social security)/i.test(responseText),
    addressesIssue: responseText.toLowerCase().includes(context.category) ||
                     responseText.length > 100,
    professionalTone: !/(!{2,}|CAPS{3,})/g.test(responseText)
  };
 
  const passedChecks = Object.values(qualityChecks).filter(v => v).length;
  const qualityScore = passedChecks / Object.keys(qualityChecks).length;
 
  const needsReview = qualityScore < 0.8 ||
                      context.sentiment === 'negative' ||
                      context.priority === 'urgent';
 
  return [{
    json: {
      ticket_id: context.ticket_id,
      original_message: context.customer_message,
      ai_response: responseText,
      quality_checks: qualityChecks,
      quality_score: qualityScore,
      needs_human_review: needsReview,
      auto_send_eligible: qualityScore >= 0.9 && !needsReview
    }
  }];
}
 
// 4. Coada de review uman (pentru raspunsurile marcate)
const humanReviewNode = {
  name: "Send to Review Queue",
  type: "n8n-nodes-base.slack",
  parameters: {
    operation: "postMessage",
    channel: "#support-review",
    text: "🔍 *Response Review Needed*",
    attachments: [
      {
        color: "={{$json.quality_score >= 0.8 ? 'warning' : 'danger'}}",
        fields: [
          { title: "Ticket ID", value: "={{$json.ticket_id}}", short: true },
          { title: "Quality Score", value: "={{($json.quality_score * 100).toFixed(0)}}%", short: true },
          { title: "Customer Message", value: "={{$json.original_message.substring(0, 200)}}..." },
          { title: "AI Draft Response", value: "={{$json.ai_response.substring(0, 500)}}..." }
        ],
        actions: [
          { type: "button", text: "✅ Approve & Send", value: "approve_{{$json.ticket_id}}" },
          { type: "button", text: "✏️ Edit", value: "edit_{{$json.ticket_id}}" },
          { type: "button", text: "❌ Reject", value: "reject_{{$json.ticket_id}}" }
        ]
      }
    ]
  }
};
 
// 5. Trimitere automata raspunsuri aprobate
const autoSendResponse = {
  name: "Send Response",
  type: "n8n-nodes-base.emailSend",
  parameters: {
    fromEmail: "support@company.com",
    toEmail: "={{$json.customer_email}}",
    subject: "Re: {{$json.subject}}",
    text: "={{$json.ai_response}}",
    options: {
      replyTo: "support@company.com"
    }
  }
};

Workflow de escaladare

Gestioneaza escaladarea tichetelor automat:

// Ticket Escalation Workflow
 
// 1. Monitorizare triggere de escaladare
const escalationTriggers = {
  name: "Check Escalation Needed",
  type: "n8n-nodes-base.function",
  parameters: {
    functionCode: `
      const ticket = items[0].json;
 
      // Reguli de escaladare
      const rules = [
        {
          name: 'sla_breach',
          condition: () => {
            const ageHours = (Date.now() - new Date(ticket.created_at).getTime()) / (1000 * 60 * 60);
            const slaHours = { urgent: 1, high: 4, normal: 24, low: 48 };
            return ageHours > slaHours[ticket.priority] && ticket.status !== 'resolved';
          },
          escalation_level: 'manager',
          reason: 'Incalcare SLA iminenta'
        },
        {
          name: 'negative_sentiment_unresolved',
          condition: () => ticket.sentiment === 'negative' && ticket.replies > 2 && ticket.status !== 'resolved',
          escalation_level: 'senior_agent',
          reason: 'Sentiment negativ cu raspunsuri multiple'
        },
        {
          name: 'customer_requested',
          condition: () => ticket.body.toLowerCase().includes('speak to manager') ||
                          ticket.body.toLowerCase().includes('escalate'),
          escalation_level: 'manager',
          reason: 'Clientul a solicitat escaladare'
        },
        {
          name: 'vip_customer',
          condition: () => ticket.customer_tier === 'enterprise' && ticket.priority !== 'low',
          escalation_level: 'senior_agent',
          reason: 'Tichet client VIP'
        },
        {
          name: 'technical_complexity',
          condition: () => ticket.category === 'technical' &&
                          (ticket.body.includes('data loss') || ticket.body.includes('security')),
          escalation_level: 'engineering',
          reason: 'Problema tehnica complexa'
        }
      ];
 
      // Verifica fiecare regula
      const triggeredRules = rules.filter(rule => {
        try {
          return rule.condition();
        } catch {
          return false;
        }
      });
 
      return [{
        json: {
          ...ticket,
          escalation_needed: triggeredRules.length > 0,
          escalation_rules_triggered: triggeredRules.map(r => ({
            name: r.name,
            level: r.escalation_level,
            reason: r.reason
          })),
          highest_escalation_level: triggeredRules.length > 0 ?
            getHighestLevel(triggeredRules.map(r => r.escalation_level)) : null
        }
      }];
 
      function getHighestLevel(levels) {
        const hierarchy = ['senior_agent', 'manager', 'engineering', 'executive'];
        return levels.sort((a, b) => hierarchy.indexOf(b) - hierarchy.indexOf(a))[0];
      }
    `
  }
};
 
// 2. Executare escaladare
function executeEscalation(items) {
  const ticket = items[0].json;
 
  if (!ticket.escalation_needed) {
    return [{ json: { ...ticket, escalated: false } }];
  }
 
  const escalationActions = {
    senior_agent: {
      notify: ['senior-support@company.com'],
      slack_channel: '#senior-support',
      priority_boost: true
    },
    manager: {
      notify: ['support-manager@company.com'],
      slack_channel: '#support-managers',
      priority_boost: true,
      auto_respond: true
    },
    engineering: {
      notify: ['engineering-oncall@company.com'],
      slack_channel: '#engineering-support',
      create_jira: true
    },
    executive: {
      notify: ['vp-support@company.com', 'cto@company.com'],
      slack_channel: '#executive-escalations',
      priority_boost: true,
      auto_respond: true
    }
  };
 
  const actions = escalationActions[ticket.highest_escalation_level] || escalationActions.manager;
 
  return [{
    json: {
      ...ticket,
      escalated: true,
      escalation_level: ticket.highest_escalation_level,
      escalation_actions: actions,
      escalated_at: new Date().toISOString(),
      escalation_reasons: ticket.escalation_rules_triggered.map(r => r.reason)
    }
  }];
}
 
// 3. Notificare destinatari escaladare
const notifyEscalation = {
  name: "Notify via Slack",
  type: "n8n-nodes-base.slack",
  parameters: {
    operation: "postMessage",
    channel: "={{$json.escalation_actions.slack_channel}}",
    text: "🚨 *Ticket Escalated*",
    attachments: [
      {
        color: "danger",
        fields: [
          { title: "Ticket ID", value: "={{$json.ticket_id}}", short: true },
          { title: "Escalation Level", value: "={{$json.escalation_level}}", short: true },
          { title: "Customer", value: "={{$json.customer_email}}", short: true },
          { title: "Priority", value: "={{$json.priority}}", short: true },
          { title: "Reasons", value: "={{$json.escalation_reasons.join('\\n')}}" },
          { title: "Subject", value: "={{$json.subject}}" }
        ],
        actions: [
          { type: "button", text: "View Ticket", url: "={{$env.HELPDESK_URL}}/tickets/{{$json.ticket_id}}" },
          { type: "button", text: "Take Ownership", value: "claim_{{$json.ticket_id}}" }
        ]
      }
    ]
  }
};
 
// 4. Raspuns automat pentru tichetele escaladate
const escalationAutoResponse = {
  name: "Send Escalation Notice to Customer",
  type: "n8n-nodes-base.emailSend",
  parameters: {
    fromEmail: "support@company.com",
    toEmail: "={{$json.customer_email}}",
    subject: "Re: {{$json.subject}} - Your ticket has been escalated",
    text: `Dear Customer,
 
Thank you for your patience. We understand the importance of your request and have escalated your ticket to our {{$json.escalation_level.replace('_', ' ')}} team for priority handling.
 
You can expect to hear from a specialist within the next {{$json.priority === 'urgent' ? '30 minutes' : '2 hours'}}.
 
We apologize for any inconvenience and appreciate your understanding.
 
Best regards,
Customer Support Team
 
Ticket Reference: {{$json.ticket_id}}`
  }
};

Dashboard analytics suport

Urmareste metricile de suport:

// Support Analytics Workflow
 
// 1. Colectare metrici zilnice
const dailyMetricsTrigger = {
  name: "Daily Metrics Trigger",
  type: "n8n-nodes-base.scheduleTrigger",
  parameters: {
    rule: {
      interval: [{ field: "hours", hoursInterval: 24 }]
    }
  }
};
 
// 2. Colectare metrici
async function collectSupportMetrics(items) {
  const yesterday = new Date(Date.now() - 24 * 60 * 60 * 1000);
  const today = new Date();
 
  // Ar interoga baza de date helpdesk reala
  const metrics = {
    period: {
      start: yesterday.toISOString(),
      end: today.toISOString()
    },
    tickets: {
      total_received: 150,
      total_resolved: 142,
      total_escalated: 8,
      by_channel: {
        email: 80,
        chat: 45,
        phone: 20,
        social: 5
      },
      by_category: {
        technical: 55,
        billing: 35,
        account: 25,
        sales: 20,
        general: 15
      },
      by_priority: {
        urgent: 5,
        high: 25,
        normal: 100,
        low: 20
      }
    },
    response_times: {
      first_response_avg_minutes: 15,
      first_response_median_minutes: 8,
      resolution_avg_hours: 4.5,
      resolution_median_hours: 2.1
    },
    sla: {
      first_response_compliance: 0.94,
      resolution_compliance: 0.89,
      breaches: 12
    },
    customer_satisfaction: {
      csat_score: 4.2,
      nps: 45,
      surveys_sent: 142,
      surveys_completed: 68,
      response_rate: 0.48
    },
    agent_performance: {
      tickets_per_agent_avg: 18,
      top_performers: [
        { name: 'Alice', tickets_resolved: 28, csat: 4.8 },
        { name: 'Bob', tickets_resolved: 25, csat: 4.5 }
      ]
    },
    ai_automation: {
      auto_resolved: 35,
      auto_resolved_rate: 0.23,
      ai_responses_sent: 89,
      ai_responses_approved: 82,
      approval_rate: 0.92
    }
  };
 
  return [{ json: metrics }];
}
 
// 3. Stocare metrici
const storeMetrics = {
  name: "Store Metrics",
  type: "n8n-nodes-base.postgres",
  parameters: {
    operation: "insert",
    table: "support_metrics",
    columns: "period_start, period_end, metrics_json, created_at"
  }
};
 
// 4. Generare raport
function generateReport(items) {
  const metrics = items[0].json;
 
  const report = {
    title: `Raport zilnic suport - ${new Date().toLocaleDateString()}`,
    summary: {
      tickets_handled: metrics.tickets.total_received,
      resolution_rate: ((metrics.tickets.total_resolved / metrics.tickets.total_received) * 100).toFixed(1) + '%',
      avg_response_time: `${metrics.response_times.first_response_avg_minutes} minute`,
      customer_satisfaction: `${metrics.customer_satisfaction.csat_score}/5`
    },
    highlights: [],
    alerts: [],
    recommendations: []
  };
 
  // Adauga highlight-uri
  if (metrics.sla.first_response_compliance > 0.95) {
    report.highlights.push('Conformitate SLA excelenta la primul raspuns');
  }
  if (metrics.ai_automation.auto_resolved_rate > 0.2) {
    report.highlights.push(`Automatizarea AI a rezolvat ${metrics.ai_automation.auto_resolved} tichete`);
  }
 
  // Adauga alerte
  if (metrics.sla.breaches > 10) {
    report.alerts.push(`${metrics.sla.breaches} incalcari SLA ieri`);
  }
  if (metrics.customer_satisfaction.csat_score < 4.0) {
    report.alerts.push('Scor CSAT sub tinta');
  }
 
  // Adauga recomandari
  if (metrics.tickets.by_category.technical > metrics.tickets.total_received * 0.4) {
    report.recommendations.push('Ia in considerare crearea de documentatie tehnica suplimentara pentru a reduce volumul de tichete');
  }
 
  return [{ json: report }];
}
 
// 5. Trimitere raport
const sendReport = {
  name: "Send Daily Report",
  type: "n8n-nodes-base.slack",
  parameters: {
    operation: "postMessage",
    channel: "#support-metrics",
    text: "*{{$json.title}}*",
    attachments: [
      {
        color: "good",
        title: "Sumar",
        fields: [
          { title: "Tichete gestionate", value: "={{$json.summary.tickets_handled}}", short: true },
          { title: "Rata rezolvare", value: "={{$json.summary.resolution_rate}}", short: true },
          { title: "Timp mediu raspuns", value: "={{$json.summary.avg_response_time}}", short: true },
          { title: "CSAT", value: "={{$json.summary.customer_satisfaction}}", short: true }
        ]
      }
    ]
  }
};

Concluzie

n8n permite automatizari puternice de suport clienti, de la rutare tichete la raspunsuri generate cu AI. Construieste sisteme inteligente de clasificare, implementeaza workflow-uri de escaladare si urmareste metrici detaliate. Incepe cu rutarea de baza si adauga treptat capabilitati AI si analytics avansat pe masura ce operatiunea de suport creste.

Ai nevoie de ajutor cu conformitatea EU AI Act sau securitatea AI?

Programeaza o consultatie gratuita de 30 de minute. Fara obligatii.

Programeaza un Apel

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.