Alert your team in Slack when a VIP customer opens a Gorgias ticket using an agent skill

low complexityCost: Usage-based

Prerequisites

Prerequisites
  • Claude Code, Cursor, or another AI coding agent that supports skills
  • GORGIAS_EMAIL, GORGIAS_API_KEY, GORGIAS_DOMAIN environment variables
  • SLACK_BOT_TOKEN with chat:write scope
  • SLACK_CHANNEL_ID — the channel ID for VIP escalation alerts
  • VIP_LTV_THRESHOLD — minimum lifetime spend to qualify as VIP (default: 500)

Overview

This agent skill polls Gorgias for recently opened tickets, checks each customer's order history to determine if they meet your VIP threshold, and posts a rich Slack alert with customer context when they do. Because the skill runs as a batch job rather than in response to a webhook, it's ideal for teams that want VIP alerting without setting up any webhook infrastructure.

Step 1: Create the skill directory

mkdir -p .claude/skills/vip-alert/scripts

Step 2: Write the SKILL.md

Create .claude/skills/vip-alert/SKILL.md:

---
name: vip-alert
description: Checks recent Gorgias support tickets for high-value customers and posts a Slack alert with ticket details and customer LTV when a VIP is detected.
disable-model-invocation: true
allowed-tools: Bash(python *)
---
 
Check for VIP customer tickets and alert the team:
 
1. Run: `python $SKILL_DIR/scripts/alert.py`
2. Review output — it lists each ticket checked and whether an alert was sent
3. Check your Slack channel to verify the alert format looks right

Step 3: Write the alert script

Create .claude/skills/vip-alert/scripts/alert.py:

#!/usr/bin/env python3
"""
Gorgias VIP Escalation Alert
Polls recent tickets → checks customer LTV → posts Slack alert for VIPs.
"""
import os
import json
from datetime import datetime, timezone, timedelta
from pathlib import Path
 
try:
    import requests
    from slack_sdk import WebClient
except ImportError:
    os.system("pip install requests slack_sdk -q")
    import requests
    from slack_sdk import WebClient
 
GORGIAS_EMAIL  = os.environ["GORGIAS_EMAIL"]
GORGIAS_KEY    = os.environ["GORGIAS_API_KEY"]
GORGIAS_DOMAIN = os.environ["GORGIAS_DOMAIN"]
SLACK_TOKEN    = os.environ["SLACK_BOT_TOKEN"]
SLACK_CHANNEL  = os.environ["SLACK_CHANNEL_ID"]
VIP_THRESHOLD  = float(os.environ.get("VIP_LTV_THRESHOLD", "500"))
 
BASE_URL = f"https://{GORGIAS_DOMAIN}.gorgias.com/api"
AUTH     = (GORGIAS_EMAIL, GORGIAS_KEY)
 
# Track alerted tickets to avoid duplicate Slack messages
SEEN_FILE = Path(__file__).parent / ".alerted_tickets.json"
alerted: set = set(json.loads(SEEN_FILE.read_text())) if SEEN_FILE.exists() else set()
 
slack = WebClient(token=SLACK_TOKEN)
 
 
def get_recent_tickets(minutes: int = 90) -> list:
    """Fetch tickets created in the last N minutes."""
    since = datetime.now(timezone.utc) - timedelta(minutes=minutes)
    resp = requests.get(
        f"{BASE_URL}/tickets",
        auth=AUTH,
        params={"status": "open", "limit": 50},
    )
    resp.raise_for_status()
    tickets = resp.json().get("data", [])
 
    result = []
    for t in tickets:
        created_raw = t.get("created_datetime", "")
        if not created_raw:
            continue
        try:
            created = datetime.fromisoformat(created_raw.replace("Z", "+00:00"))
            if created >= since:
                result.append(t)
        except ValueError:
            continue
    return result
 
 
def get_customer_ltv(customer_id: int) -> tuple[float, int]:
    """Return (total_spent, order_count) from Gorgias customer meta."""
    resp = requests.get(f"{BASE_URL}/customers/{customer_id}", auth=AUTH)
    if not resp.ok:
        return 0.0, 0
    meta = resp.json().get("meta", {})
    spent = float(meta.get("shopify_total_spent") or 0)
    orders = int(meta.get("shopify_orders_count") or 0)
    return spent, orders
 
 
def is_vip_tagged(ticket: dict) -> bool:
    customer_tags = ticket.get("requester", {}).get("meta", {}).get("tags", [])
    return any(t.get("name") == "vip" for t in customer_tags)
 
 
def post_slack_alert(ticket: dict, ltv: float, order_count: int) -> None:
    subject  = ticket.get("subject", "(no subject)")
    body     = (ticket.get("messages") or [{}])[0].get("body_text", "")[:400]
    customer = ticket.get("requester", {})
    name     = customer.get("name") or customer.get("email", "Unknown")
    email    = customer.get("email", "")
    ticket_url = f"https://{GORGIAS_DOMAIN}.gorgias.com/app/ticket/{ticket['id']}"
 
    blocks = [
        {
            "type": "header",
            "text": {"type": "plain_text", "text": "🚨 VIP Customer — New Support Ticket"},
        },
        {
            "type": "section",
            "fields": [
                {"type": "mrkdwn", "text": f"*Customer*\n{name}"},
                {"type": "mrkdwn", "text": f"*Email*\n{email}"},
                {"type": "mrkdwn", "text": f"*Lifetime Value*\n${ltv:,.0f}"},
                {"type": "mrkdwn", "text": f"*Total Orders*\n{order_count}"},
                {"type": "mrkdwn", "text": f"*Subject*\n{subject}"},
            ],
        },
        {
            "type": "section",
            "text": {"type": "mrkdwn", "text": f"*Message*\n{body}"},
        },
        {
            "type": "actions",
            "elements": [
                {
                    "type": "button",
                    "text": {"type": "plain_text", "text": "Open in Gorgias"},
                    "url": ticket_url,
                    "style": "primary",
                }
            ],
        },
    ]
 
    slack.chat_postMessage(
        channel=SLACK_CHANNEL,
        text=f"🚨 VIP ticket from {name}: {subject}",
        blocks=blocks,
    )
 
 
def main() -> None:
    print("Checking recent tickets for VIP customers...")
    tickets = get_recent_tickets(minutes=90)
    print(f"Found {len(tickets)} recent ticket(s)\n")
 
    alerts_sent = 0
    for ticket in tickets:
        ticket_id = str(ticket["id"])
        if ticket_id in alerted:
            continue
 
        customer_id = ticket.get("requester", {}).get("id")
        ltv, order_count = get_customer_ltv(customer_id) if customer_id else (0.0, 0)
        vip_tagged = is_vip_tagged(ticket)
        is_vip = vip_tagged or ltv >= VIP_THRESHOLD or order_count >= 5
 
        subject = ticket.get("subject", "(no subject)")
        print(f"  #{ticket['id']}  {subject[:50]!r}  LTV=${ltv:.0f}  VIP={is_vip}")
 
        if is_vip:
            post_slack_alert(ticket, ltv, order_count)
            alerted.add(ticket_id)
            alerts_sent += 1
            print(f"    → Slack alert sent")
 
    # Persist alerted set (keep last 500 entries to avoid unbounded growth)
    trimmed = list(alerted)[-500:]
    SEEN_FILE.write_text(json.dumps(trimmed))
 
    print(f"\nDone. {alerts_sent} alert(s) sent.")
 
 
if __name__ == "__main__":
    main()

Step 4: Run the skill

# Via Claude Code
/vip-alert
 
# Or directly
VIP_LTV_THRESHOLD=750 python .claude/skills/vip-alert/scripts/alert.py

Step 5: Schedule it

# crontab -e — run every 30 minutes during business hours
*/30 8-18 * * 1-5 cd /path/to/project && python .claude/skills/vip-alert/scripts/alert.py

Or use GitHub Actions with the same pattern as other skills in this series.

Cost

  • No Claude API calls (uses Gorgias and Slack APIs directly)
  • Slack SDK: free
  • Gorgias API: included in your plan

Need help implementing this?

We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.