Systems Library / Customer Service / How to Automate Ticket Follow-Up After Resolution
Customer Service ticket management

How to Automate Ticket Follow-Up After Resolution

Follow up with customers after ticket resolution to confirm satisfaction.

Jay Banlasan

Jay Banlasan

The AI Systems Guy

When you automate ticket follow up after resolution for satisfaction checks, you catch problems that customers did not bother to report again. I build these because 80% of unsatisfied customers never reopen a ticket. They just leave. A simple follow-up 24 hours after resolution catches them before they churn.

The system waits for resolution, sends a personalized follow-up, and routes unhappy responses back to the team.

What You Need Before Starting

Step 1: Trigger Follow-Up on Resolution

Watch for tickets moving to "resolved" status:

from flask import Flask, request
from datetime import datetime

app = Flask(__name__)

@app.route("/ticket-resolved", methods=["POST"])
def on_resolved():
    ticket = request.json
    schedule_followup(
        ticket_id=ticket["id"],
        customer_email=ticket["customer_email"],
        customer_name=ticket["customer_name"],
        resolved_at=datetime.now(),
        delay_hours=24
    )
    return "scheduled", 200

def schedule_followup(ticket_id, customer_email, customer_name, resolved_at, delay_hours):
    conn = sqlite3.connect("followups.db")
    send_at = resolved_at + timedelta(hours=delay_hours)
    conn.execute("""
        INSERT INTO scheduled_followups (ticket_id, email, name, send_at, status)
        VALUES (?, ?, ?, ?, 'pending')
    """, (ticket_id, customer_email, customer_name, send_at.isoformat()))
    conn.commit()

Step 2: Generate Personalized Follow-Up Messages

import anthropic

client = anthropic.Anthropic()

def generate_followup(ticket):
    response = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=200,
        messages=[{
            "role": "user",
            "content": f"""Write a brief follow-up email for a support ticket that was resolved.
Keep it under 100 words. Friendly, not corporate. Ask if the issue is truly fixed.
Include a simple 1-5 satisfaction rating request.

Customer name: {ticket['customer_name']}
Original issue: {ticket['subject']}
Resolution: {ticket['resolution_summary']}"""
        }]
    )
    return response.content[0].text

Step 3: Send Follow-Ups on Schedule

Run a job that processes pending follow-ups:

import sqlite3
from datetime import datetime, timedelta
import smtplib
from email.mime.text import MIMEText

def process_pending_followups():
    conn = sqlite3.connect("followups.db")
    now = datetime.now().isoformat()

    pending = conn.execute(
        "SELECT * FROM scheduled_followups WHERE status = 'pending' AND send_at <= ?",
        (now,)
    ).fetchall()

    for followup in pending:
        ticket = get_ticket(followup["ticket_id"])
        message = generate_followup(ticket)
        send_followup_email(followup["email"], followup["name"], message, followup["ticket_id"])

        conn.execute(
            "UPDATE scheduled_followups SET status = 'sent', sent_at = ? WHERE id = ?",
            (now, followup["id"])
        )
    conn.commit()

Step 4: Collect and Route Feedback

When a customer responds negatively, reopen or escalate:

@app.route("/followup-response", methods=["POST"])
def handle_feedback():
    data = request.json
    ticket_id = data["ticket_id"]
    rating = data["rating"]
    comment = data.get("comment", "")

    save_feedback(ticket_id, rating, comment)

    if rating <= 2:
        reopen_ticket(ticket_id, reason=f"Customer unsatisfied (rating: {rating}). Comment: {comment}")
        notify_team(ticket_id, rating, comment)

    return "received", 200

Step 5: Track Satisfaction Trends

def get_satisfaction_report(days=30):
    conn = sqlite3.connect("followups.db")
    start = (datetime.now() - timedelta(days=days)).isoformat()

    sent = conn.execute("SELECT COUNT(*) FROM scheduled_followups WHERE sent_at > ?", (start,)).fetchone()[0]
    responded = conn.execute("SELECT COUNT(*) FROM feedback WHERE created_at > ?", (start,)).fetchone()[0]
    avg_rating = conn.execute("SELECT AVG(rating) FROM feedback WHERE created_at > ?", (start,)).fetchone()[0]
    reopened = conn.execute(
        "SELECT COUNT(*) FROM feedback WHERE rating <= 2 AND created_at > ?", (start,)
    ).fetchone()[0]

    return {
        "followups_sent": sent,
        "response_rate": round(responded / sent * 100, 1) if sent else 0,
        "avg_satisfaction": round(avg_rating or 0, 1),
        "reopened_tickets": reopened
    }

What to Build Next

Segment follow-up timing by issue type. Simple password resets can get a follow-up after 2 hours. Complex technical issues should wait 48 hours to give the customer time to verify the fix.

Related Reading

Want this system built for your business?

Get a free assessment. We will map every system your business needs and show you the ROI.

Get Your Free Assessment

Related Systems