Build an AI Customer Support Agent That Handles 80% of Tickets Automatically
Automate14 min read·April 10, 2026·--

Build an AI Customer Support Agent That Handles 80% of Tickets Automatically

A complete guide to building a support system where AI handles FAQs, order status, billing questions, and returns — passing only genuinely complex issues to humans.

@
@kivorablog
April 10, 2026
Share

The 80/20 of Customer Support


In any support queue, roughly 80% of tickets are variations of the same 10–20 questions. The other 20% are genuinely complex and need a human.


Most companies pay humans to answer the 80%. That's an expensive, slow, and demoralising use of human intelligence. Your job is to automate the 80% so your humans can focus on the 20% that actually needs them.


Common 80% Questions (Automate These)


CategoryExample Question
Order status"Where is my order?"
Account access"I forgot my password"
Billing"When is my next payment?"
Product info"Does this work on mobile?"
Returns"How do I return this?"
Shipping"Do you ship to Nigeria?"
Refunds"When will I get my refund?"

The 20% (Keep for Humans)


  • Angry customers who have had a bad experience
  • Complex technical issues
  • Billing disputes
  • VIP customer requests
  • Anything requiring judgment calls

Free Stack for AI Support

ToolPurposeFree Tier
GroqAI responses14,400 req/day
SupabaseKnowledge base + ticket storage500MB
n8nWorkflow orchestrationFree (self-hosted)
Tawk.toLive chat widgetFree forever
GmailEmail supportFree

Step 1: Build the Knowledge Base

Your AI is only as good as the information it has. Create a structured knowledge base in Supabase:

create table knowledge_base (
  id         uuid primary key default gen_random_uuid(),
  category   text not null,
  question   text not null,
  answer     text not null,
  keywords   text[],
  updated_at timestamptz default now()
);

-- Insert your FAQs
insert into knowledge_base (category, question, answer, keywords) values
('shipping', 'How long does delivery take?',
 'Standard delivery takes 3–5 business days within Lagos and 5–7 days for other states. Express delivery (1–2 days) is available for ₦2,000 extra.',
 ARRAY['delivery', 'shipping', 'how long', 'when will', 'arrive']),

('returns', 'What is your return policy?',
 'We accept returns within 14 days of delivery. Items must be unused and in original packaging. Initiate a return at returns.yourstore.com or reply to your order confirmation email.',
 ARRAY['return', 'refund', 'send back', 'exchange', 'wrong item']),

('account', 'How do I reset my password?',
 'Go to yourstore.com/forgot-password and enter your email. You will receive a reset link within 5 minutes. Check your spam folder if you don''t see it.',
 ARRAY['password', 'forgot', 'login', 'access', 'locked out']);

Step 2: The AI Response Engine

// lib/support-ai.js
import { createClient } from '@supabase/supabase-js'
import Groq from 'groq-sdk'

const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_SERVICE_KEY)
const groq     = new Groq({ apiKey: process.env.GROQ_API_KEY })

export async function handleSupportMessage(message, conversationHistory = []) {
  // Step 1: Search knowledge base for relevant articles
  const { data: articles } = await supabase
    .from('knowledge_base')
    .select('category, question, answer')
    .textSearch('question', message, { type: 'websearch' })
    .limit(5)

  const knowledgeContext = articles?.length
    ? articles.map(a => `Q: ${a.question}\nA: ${a.answer}`).join('\n\n')
    : 'No specific articles found for this query.'

  // Step 2: Generate response with context
  const response = await groq.chat.completions.create({
    model: 'llama-3.3-70b-versatile',
    messages: [
      {
        role: 'system',
        content: `You are a helpful customer support agent for YourStore.

RULES:
1. Only answer questions using the knowledge base provided
2. If the answer isn't in the knowledge base, say "I'll connect you with our team for this one"
3. Be conversational but professional
4. Keep responses under 150 words
5. If the customer is angry, acknowledge their frustration before answering
6. Never make up information about orders, prices, or policies

KNOWLEDGE BASE:
${knowledgeContext}

CONFIDENCE SCORING:
At the end of your response, on a new line, write: CONFIDENCE: [0-100]
- 90-100: Answer is clearly in the knowledge base
- 60-89: Partial match, answer is reasonable
- 0-59: Escalate to human agent`
      },
      ...conversationHistory.slice(-6),
      { role: 'user', content: message }
    ]
  })

  const fullResponse = response.choices[0].message.content
  const confidenceMatch = fullResponse.match(/CONFIDENCE:\s*(\d+)/)
  const confidence      = confidenceMatch ? parseInt(confidenceMatch[1]) : 50
  const cleanResponse   = fullResponse.replace(/CONFIDENCE:\s*\d+/, '').trim()

  return {
    message:       cleanResponse,
    confidence,
    shouldEscalate: confidence < 60,
    suggestedCategory: articles?.[0]?.category || 'general'
  }
},

Step 3: The Escalation Logic

// app/api/support/route.js
import { handleSupportMessage } from '@/lib/support-ai'
import { supabaseAdmin } from '@/lib/supabase'

export async function POST(req) {
  const { message, ticketId, conversationHistory } = await req.json()

  const result = await handleSupportMessage(message, conversationHistory)

  // Log the interaction
  await supabaseAdmin.from('support_logs').insert({
    ticket_id:       ticketId,
    user_message:    message,
    ai_response:     result.message,
    confidence:      result.confidence,
    was_escalated:   result.shouldEscalate,
  })

  if (result.shouldEscalate) {
    // Notify human agent
    await notifyHumanAgent(ticketId, message, result.message)

    return Response.json({
      message:   result.message + "\n\nI've flagged this for our team and someone will follow up shortly.",
      escalated: true,
    })
  }

  return Response.json({
    message:   result.message,
    escalated: false,
    confidence: result.confidence,
  })
},

async function notifyHumanAgent(ticketId, customerMessage, aiDraft) {
  // Send to your team via Slack, email, or your helpdesk
  await fetch(process.env.SLACK_WEBHOOK, {
    method: 'POST',
    body: JSON.stringify({
      text: `🔴 Escalated ticket #${ticketId}\n*Customer:* ${customerMessage}\n*AI draft:* ${aiDraft}`
    })
  })
},

Measuring the Impact

Track these metrics monthly:

MetricTargetHow to Measure
AI resolution rate75–85%Tickets resolved without escalation / total tickets
Average response time< 30 secondsTimestamp difference: message received → AI reply
Customer satisfaction> 4.0/5Post-resolution survey
Escalation rate< 25%Escalated tickets / total tickets
False positive rate< 5%Escalated tickets that AI could have handled
Read more on Kivora Blog

Read more on Kivora Blog

Get started →