Flag repeat customer contacts in Gorgias and alert Slack using an agent skill
Prerequisites
- Claude Code, Cursor, or another AI coding agent that supports skills
GORGIAS_EMAIL,GORGIAS_API_KEY,GORGIAS_DOMAINenvironment variablesSLACK_WEBHOOK_URLenvironment variable (Slack Incoming Webhook)- Python 3.9+ with
requestsinstalled
Overview
This agent skill scans your open Gorgias tickets, groups them by customer, and identifies anyone who has opened 3 or more tickets in the past 7 days. When it finds a repeat contact, it tags the latest ticket in Gorgias and posts a Slack alert with the customer's recent ticket history. You can run it on-demand or schedule it with cron.
Step 1: Create the skill directory
mkdir -p .claude/skills/repeat-contact/scriptsStep 2: Write the SKILL.md
Create .claude/skills/repeat-contact/SKILL.md:
---
name: repeat-contact
description: Scans Gorgias tickets for customers with 3+ tickets in the past 7 days, tags them as repeat contacts, and posts a Slack alert.
disable-model-invocation: true
allowed-tools: Bash(python *)
---
Flag repeat customer contacts:
1. Run: `python $SKILL_DIR/scripts/flag_repeats.py`
2. Review the output — it shows flagged customers and their recent tickets
3. Adjust THRESHOLD or WINDOW_DAYS environment variables to tune sensitivityStep 3: Write the detection script
Create .claude/skills/repeat-contact/scripts/flag_repeats.py:
#!/usr/bin/env python3
"""
Repeat Contact Flagging
Scans Gorgias tickets, identifies repeat contacts, tags them, and alerts Slack.
"""
import os
import json
from datetime import datetime, timedelta, timezone
try:
import requests
except ImportError:
os.system("pip install requests -q")
import requests
GORGIAS_EMAIL = os.environ["GORGIAS_EMAIL"]
GORGIAS_KEY = os.environ["GORGIAS_API_KEY"]
GORGIAS_DOMAIN = os.environ["GORGIAS_DOMAIN"]
SLACK_WEBHOOK = os.environ["SLACK_WEBHOOK_URL"]
BASE_URL = f"https://{GORGIAS_DOMAIN}.gorgias.com/api"
AUTH = (GORGIAS_EMAIL, GORGIAS_KEY)
THRESHOLD = int(os.environ.get("REPEAT_THRESHOLD", "3"))
WINDOW_DAYS = int(os.environ.get("REPEAT_WINDOW_DAYS", "7"))
def get_recent_tickets(limit: int = 100) -> list:
"""Fetch recent open and closed tickets within the detection window."""
cutoff = datetime.now(timezone.utc) - timedelta(days=WINDOW_DAYS)
resp = requests.get(
f"{BASE_URL}/tickets",
auth=AUTH,
params={"limit": limit, "order_by": "created_datetime:desc"},
)
resp.raise_for_status()
tickets = resp.json().get("data", [])
return [
t for t in tickets
if datetime.fromisoformat(
t["created_datetime"].replace("Z", "+00:00")
) >= cutoff
]
def group_by_customer(tickets: list) -> dict:
"""Group tickets by customer ID."""
groups = {}
for ticket in tickets:
cust = ticket.get("customer")
if not cust or not cust.get("id"):
continue
cid = cust["id"]
if cid not in groups:
groups[cid] = {
"customer_id": cid,
"name": cust.get("firstname", cust.get("email", "Unknown")),
"email": cust.get("email", "unknown"),
"tickets": [],
}
groups[cid]["tickets"].append({
"id": ticket["id"],
"subject": ticket.get("subject", "(no subject)"),
"status": ticket.get("status", "unknown"),
"created": ticket["created_datetime"],
})
return groups
def tag_ticket(ticket_id: int, tag: str = "repeat-contact") -> None:
"""Apply a tag to a ticket in Gorgias."""
resp = requests.put(
f"{BASE_URL}/tickets/{ticket_id}",
auth=AUTH,
json={"tags": [{"name": tag}]},
)
resp.raise_for_status()
def send_slack_alert(customer: dict) -> None:
"""Post a repeat contact alert to Slack."""
tickets_list = "\n".join(
f" - #{t['id']}: {t['subject']} ({t['status']})"
for t in customer["tickets"]
)
latest = customer["tickets"][0]
text = (
f":rotating_light: *Repeat Contact Flagged*\n\n"
f"*{customer['name']}* ({customer['email']}) has opened "
f"*{len(customer['tickets'])} tickets* in the last {WINDOW_DAYS} days.\n\n"
f"*Recent tickets:*\n{tickets_list}\n\n"
f"<https://{GORGIAS_DOMAIN}.gorgias.com/app/ticket/{latest['id']}"
f"|View latest ticket in Gorgias>"
)
requests.post(SLACK_WEBHOOK, json={"text": text})
def main() -> None:
print(f"Scanning for customers with {THRESHOLD}+ tickets "
f"in the last {WINDOW_DAYS} days...\n")
tickets = get_recent_tickets()
print(f"Fetched {len(tickets)} tickets from the last {WINDOW_DAYS} days")
groups = group_by_customer(tickets)
flagged = {
cid: data for cid, data in groups.items()
if len(data["tickets"]) >= THRESHOLD
}
if not flagged:
print("No repeat contacts found. All clear.")
return
print(f"Found {len(flagged)} repeat contact(s):\n")
for cid, customer in flagged.items():
latest_ticket = customer["tickets"][0]
# Tag the most recent ticket
tag_ticket(latest_ticket["id"])
# Alert Slack
send_slack_alert(customer)
print(f" {customer['name']} ({customer['email']})")
print(f" {len(customer['tickets'])} tickets in {WINDOW_DAYS} days")
for t in customer["tickets"]:
print(f" #{t['id']}: {t['subject']} ({t['status']})")
print(f" -> Tagged #{latest_ticket['id']} + Slack alert sent\n")
print(f"Done. Flagged {len(flagged)} repeat contact(s).")
if __name__ == "__main__":
main()Tagging every ticket from a repeat contact creates noise. The script tags only the latest ticket so your team sees the flag on the one they're most likely to open next. Previous tickets are listed in the Slack alert for context.
Step 4: Run the skill
# Via Claude Code
/repeat-contact
# Or run directly
python .claude/skills/repeat-contact/scripts/flag_repeats.py
# With custom threshold (e.g., 5 tickets in 14 days)
REPEAT_THRESHOLD=5 REPEAT_WINDOW_DAYS=14 python .claude/skills/repeat-contact/scripts/flag_repeats.pyA typical run looks like:
Scanning for customers with 3+ tickets in the last 7 days...
Fetched 87 tickets from the last 7 days
Found 2 repeat contact(s):
Sarah M. (sarah@example.com)
4 tickets in 7 days
#14501: Can't log in to my account (open)
#14487: Still can't access order history (open)
#14463: Password reset not working (closed)
#14451: Account access issue (closed)
-> Tagged #14501 + Slack alert sent
James K. (james@example.com)
3 tickets in 7 days
#14498: Wrong item received again (open)
#14472: Missing item in my order (closed)
#14460: Shipping damage (closed)
-> Tagged #14498 + Slack alert sent
Done. Flagged 2 repeat contact(s).Step 5: Schedule it
# crontab -e — every 2 hours during business hours
0 8-18/2 * * 1-5 cd /path/to/project && python .claude/skills/repeat-contact/scripts/flag_repeats.pyEvery 2 hours is a good starting point. If your team needs faster notification, switch to the n8n approach with a webhook trigger for real-time detection. The agent skill approach is better suited for periodic sweeps.
Step 6: Tune the threshold over time
After a week of running, review the flagged contacts:
- Too many false positives? Raise
REPEAT_THRESHOLDto 4 or 5 - Missing genuine repeat contacts? Lower to 2 or shorten
REPEAT_WINDOW_DAYS - Certain customer segments always flag? Add exclusions for known high-volume accounts (agencies, resellers) by maintaining an exclude list in the script
Cost
- Gorgias API calls: 2 per run (list tickets + tag per flagged customer)
- Slack webhook: free
- Typical run: under 5 seconds, no external AI API calls needed
- Scheduled 5x/day: ~10 Gorgias API calls/day
Need help implementing this?
We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.