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.


Sistemul tau AI e conform cu EU AI Act? Evaluare gratuita de risc - afla in 2 minute →

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.