Auto-triage and tag Zendesk tickets using a Claude Code skill

low complexityCost: Usage-based

Prerequisites

Compatible agents

This skill works with any agent that supports the Claude Code skills standard, including Claude Code, Claude Cowork, OpenAI Codex, and Google Antigravity.

Prerequisites
  • One of the agents listed above
  • Zendesk account with API access enabled
  • Anthropic API key for Claude-powered classification (claude-haiku-4-5 recommended)
  • Groups configured in Zendesk for each topic area
Environment Variables
# Your Zendesk subdomain (the 'acme' part of acme.zendesk.com)
ZENDESK_SUBDOMAIN=your_value_here
# Zendesk agent email for API authentication
ZENDESK_EMAIL=your_value_here
# Zendesk API token (Admin Center > Apps and Integrations > APIs > Zendesk API)
ZENDESK_API_TOKEN=your_value_here
# Anthropic API key for Claude ticket classification
ANTHROPIC_API_KEY=your_value_here

Why a Claude Code skill?

The other approaches in this guide use keyword matching — they check if a ticket contains specific words like "invoice" or "shipping." An Claude Code skill is different. It uses Claude to understand ticket intent, so "my parcel never showed up" gets classified as shipping even without the word "shipping."

That means you can say:

  • "Triage all untagged tickets and assign them to groups"
  • "How accurate was auto-triage this week? Show me the misclassified ones."
  • "Add a new category for 'returns' and reclassify tickets from the last 24 hours"

The skill contains workflow guidelines, API reference materials, and a classification prompt template that the agent reads on demand. When you invoke the skill, Claude reads these files, writes a script on the fly, runs it, and reports results. If you ask for something different next time — a new category, a different batch size, an accuracy audit — the agent adapts without you touching any code.

How it works

The skill directory has three parts:

  1. SKILL.md — workflow guidelines telling the agent what steps to follow, which env vars to use, and what pitfalls to avoid
  2. references/ — Zendesk API patterns (search, ticket update, groups) so the agent calls the right APIs with the right parameters
  3. templates/ — a classification prompt template so ticket classification is consistent across runs

When invoked, the agent reads SKILL.md, consults the reference and template files as needed, writes a Python script, executes it, and reports what it classified. The reference files act as guardrails — the agent knows exactly which endpoints to hit and what the responses look like, so it doesn't have to guess.

What is a Claude Code skill?

An Claude Code skill is a reusable command you add to your project that Claude Code can run on demand. Skills live in a .claude/skills/ directory and are defined by a SKILL.md file that tells the agent what the skill does, when to run it, and what tools it's allowed to use.

In this skill, the agent doesn't run a pre-written script. Instead, SKILL.md provides workflow guidelines and points to reference files — API documentation, prompt templates — that the agent reads to generate and execute code itself. This is the key difference from a traditional script: the agent can adapt its approach based on what you ask for while still using the right APIs and classification prompts.

Once installed, you can invoke a skill as a slash command (e.g., /zendesk-triage), or the agent will use it automatically when you give it a task where the skill is relevant. Skills are portable — anyone who clones your repo gets the same commands.

Step 1: Create the skill directory

mkdir -p .claude/skills/zendesk-triage/{templates,references}

This creates the layout:

.claude/skills/zendesk-triage/
├── SKILL.md                          # workflow guidelines + config
├── templates/
│   └── classification-prompt.md      # Prompt template for ticket classification
└── references/
    └── zendesk-tickets-api.md        # Zendesk search, update, and groups API patterns

Step 2: Write the SKILL.md

Create .claude/skills/zendesk-triage/SKILL.md:

---
name: zendesk-triage
description: Classify untagged Zendesk tickets by topic using Claude and apply tags and group assignments
disable-model-invocation: true
allowed-tools: Bash, Read
---
 
## Goal
 
Fetch untagged open tickets from Zendesk, classify each by topic using Claude (claude-haiku-4-5), and update each ticket with the appropriate topic tag and support group assignment.
 
## Configuration
 
Read these environment variables:
 
- `ZENDESK_SUBDOMAIN` — Zendesk subdomain, e.g. "acme" for acme.zendesk.com (required)
- `ZENDESK_EMAIL` — Zendesk agent email for API auth (required)
- `ZENDESK_API_TOKEN` — Zendesk API token (required)
- `ANTHROPIC_API_KEY` — Anthropic API key for classification (required)
 
Default batch size: 50 tickets per run. The user may request a different size.
 
## Workflow
 
1. Validate that all required env vars are set. If any are missing, print which ones and exit.
2. Fetch Zendesk groups to build a group name → group ID map. See `references/zendesk-tickets-api.md`.
3. Search for open tickets that don't have any topic tag. See `references/zendesk-tickets-api.md` for the search query and response format.
4. For each untagged ticket, classify it using Claude with the prompt in `templates/classification-prompt.md`. Use claude-haiku-4-5 with max_tokens=20.
5. Map the classification label to a topic tag and group. Update the ticket via the Zendesk API with the tag and group assignment. See `references/zendesk-tickets-api.md`.
6. Print a summary of how many tickets were classified and the breakdown by topic.
 
## Important notes
 
- The classification prompt returns one of: BILLING, SHIPPING, PRODUCT, ACCOUNT, TECHNICAL, OTHER. Map these to tags: topic-billing, topic-shipping, etc.
- Authentication uses Basic Auth with `email/token:api_token` format. The `/token` suffix after the email is required.
- The Anthropic SDK (`anthropic` pip package) is required. Install it with `pip install anthropic` if not present.
- The Zendesk PUT endpoint replaces the entire tags array. Merge new tags with existing tags before updating.
- Group names must match exactly what's configured in Zendesk (Admin Center > People > Groups). The script should fetch groups dynamically rather than hardcoding IDs.
- Use only Python standard library for Zendesk API calls (`urllib.request`, `json`, `base64`). Use the `anthropic` package for classification.

Understanding the SKILL.md

Unlike a script-based skill, this SKILL.md doesn't contain a Run: command pointing to a script. Instead, it provides:

SectionPurpose
GoalTells the agent what outcome to produce
ConfigurationWhich env vars to read and what defaults to use
WorkflowNumbered steps with pointers to reference files
Important notesNon-obvious context that prevents common mistakes

The allowed-tools: Bash, Read setting lets the agent both read reference files and execute code. The agent writes its own script based on the workflow steps and reference materials.

Step 3: Add reference files

references/zendesk-tickets-api.md

Create .claude/skills/zendesk-triage/references/zendesk-tickets-api.md:

# Zendesk Tickets API Reference
 
## Authentication
 
All Zendesk API requests use Basic Auth. The username is `email/token:api_token` (note the `/token` suffix after the email address).
 
```
Authorization: Basic base64(email/token:api_token)
```
 
Base URL: `https://{subdomain}.zendesk.com/api/v2`
 
## Search for untagged open tickets
 
Find tickets that don't have any topic tag assigned yet.
 
**Request:**
 
```
GET https://{subdomain}.zendesk.com/api/v2/search.json?query=type:ticket status:open -tags:topic-billing -tags:topic-shipping -tags:topic-product -tags:topic-account -tags:topic-technical -tags:topic-uncategorized&per_page=50
Authorization: Basic <credentials>
```
 
**Parameters:**
 
- `query` — URL-encoded search string. `-tags:topic-billing` excludes tickets with that tag.
- `per_page` — results per page (max 100)
 
**Response shape:**
 
```json
{
  "results": [
    {
      "id": 48291,
      "subject": "Can't log into my account",
      "description": "I've been trying to reset my password but the link expired...",
      "tags": [],
      "status": "open",
      "group_id": null,
      "requester_id": 67890
    }
  ],
  "count": 23
}
```
 
Note: The Zendesk Search API has a 1-2 minute indexing delay. Newly created tickets may not appear immediately.
 
## Update a ticket (add tags and group)
 
**Request:**
 
```
PUT https://{subdomain}.zendesk.com/api/v2/tickets/{ticket_id}.json
Authorization: Basic <credentials>
Content-Type: application/json
```
 
**Body:**
 
```json
{
  "ticket": {
    "tags": ["existing-tag", "topic-billing"],
    "group_id": 12345
  }
}
```
 
Important: The `tags` field replaces the entire tag array. Merge existing tags with new tags before sending.
 
## List groups
 
Fetch all support groups to map group names to IDs.
 
**Request:**
 
```
GET https://{subdomain}.zendesk.com/api/v2/groups.json
Authorization: Basic <credentials>
```
 
**Response shape:**
 
```json
{
  "groups": [
    {
      "id": 12345,
      "name": "Billing Support"
    },
    {
      "id": 67890,
      "name": "Shipping Support"
    }
  ]
}
```
 
## Topic-to-group mapping
 
Default mapping (customize to match your Zendesk groups):
 
| Classification | Tag | Group Name |
|---------------|-----|------------|
| BILLING | topic-billing | Billing Support |
| SHIPPING | topic-shipping | Shipping Support |
| PRODUCT | topic-product | Product Support |
| ACCOUNT | topic-account | Account Support |
| TECHNICAL | topic-technical | Technical Support |
| OTHER | topic-uncategorized | General Support |
 
## Rate limits
 
~700 requests/minute on Professional plans. Each ticket classification requires 1 PUT call. 50 tickets = 50 API calls + 1 search + 1 groups lookup = 52 total.

templates/classification-prompt.md

Create .claude/skills/zendesk-triage/templates/classification-prompt.md:

# Ticket Classification Prompt
 
Use this prompt template when classifying each ticket with Claude.
 
## Prompt
 
```
You are a support ticket classifier. Classify this ticket into exactly one topic.
 
Subject: {subject}
Description (first 500 chars): {description_truncated}
 
Reply with exactly one of:
- BILLING — invoices, charges, payments, subscriptions, pricing, refunds
- SHIPPING — delivery, tracking, packages, lost items, shipping delays, returns
- PRODUCT — product quality, defects, wrong item, size/color issues, product questions
- ACCOUNT — login, password, account access, profile settings, two-factor auth
- TECHNICAL — errors, bugs, crashes, API issues, integrations, performance problems
- OTHER — doesn't fit any of the above
 
Reply with only the label, nothing else.
```
 
## Model settings
 
- Model: `claude-haiku-4-5-20251001`
- max_tokens: 20
- No system prompt needed — the classification instructions are in the user message
 
## Notes
 
- Truncate the description to 500 characters to keep costs low and classification fast
- Claude Haiku costs ~$0.001 per classification at these token counts
- The prompt should be customized if you add or rename categories

Step 4: Test the skill

Invoke the skill conversationally:

/zendesk-triage

Claude will read the SKILL.md, check the reference files, write a script, install the Anthropic SDK if needed, run it, and report the results. A typical run looks like:

Fetching untagged open tickets...
  Found 23 untagged tickets to classify
 
  #48291  "Can't log into my account"  →  topic-account (Account Support)
  #48295  "Invoice shows wrong amount"  →  topic-billing (Billing Support)
  #48301  "Package never arrived"  →  topic-shipping (Shipping Support)
  ...
 
Classified 23 tickets:
  topic-account: 6
  topic-billing: 5
  topic-shipping: 4
  topic-technical: 4
  topic-product: 3
  topic-uncategorized: 1

What the result looks like

What you'll get
Zendesk Ticket
SubjectCan't log into my account
Category
(uncategorized)Account Access
Priority
NormalHigh
Tags
(empty)topic-account
Group
General SupportAccount Support

Because the agent generates code on the fly, you can also make ad hoc requests:

  • "Triage only the last 10 tickets" — the agent adjusts the batch size
  • "Add a 'returns' category and reclassify tickets from today" — the agent updates the prompt and re-runs
  • "How accurate was auto-triage this week?" — the agent audits tickets where agents changed the tag after auto-assignment
Test with a few tickets first

Run the skill on 5-10 tickets initially. Spot-check the classifications in Zendesk to verify accuracy before processing larger batches. If a category is consistently wrong, adjust the prompt template.

Step 5: Schedule it (optional)

Option A: Cron + Claude CLI

# Run every 30 minutes during business hours
*/30 8-18 * * 1-5 cd /path/to/your/project && claude -p "Run /zendesk-triage" --allowedTools 'Bash(*)' 'Read(*)'

Option B: GitHub Actions + Claude

name: Zendesk Ticket Triage
on:
  schedule:
    - cron: '*/30 13-22 * * 1-5'  # 8 AM-5 PM ET, weekdays
  workflow_dispatch: {}   # Manual trigger for testing
jobs:
  triage:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: anthropics/claude-code-action@v1
        with:
          prompt: "Run /zendesk-triage"
          allowed_tools: "Bash(*),Read(*)"
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          ZENDESK_SUBDOMAIN: ${{ secrets.ZENDESK_SUBDOMAIN }}
          ZENDESK_EMAIL: ${{ secrets.ZENDESK_EMAIL }}
          ZENDESK_API_TOKEN: ${{ secrets.ZENDESK_API_TOKEN }}

Option C: Cowork Scheduled Tasks

Claude Desktop's Cowork supports built-in scheduled tasks. Open a Cowork session, type /schedule, and configure the cadence — hourly, daily, weekly, or weekdays only. Each scheduled run has full access to your connected tools, plugins, and MCP servers.

Scheduled tasks only run while your computer is awake and Claude Desktop is open. If a run is missed, Cowork executes it automatically when the app reopens. For always-on scheduling, use GitHub Actions (Option B) instead. Available on all paid plans (Pro, Max, Team, Enterprise).

AI classification adds latency compared to native triggers

Unlike keyword triggers that fire instantly, each ticket takes 1-2 seconds to classify with Claude. For instant triage on obvious keywords, combine this skill with Zendesk native triggers — let triggers handle the easy matches and use the skill to catch tickets that slip through without a topic tag.

Troubleshooting

When to use this approach

  • You need semantic classification — Claude understands "my parcel never showed up" as a shipping issue even without the word "shipping"
  • You have multilingual customers and keyword triggers fail on non-English tickets
  • You want to test category definitions before hardcoding them into Zendesk triggers
  • You want to run tasks in the background via Claude Cowork while focusing on other work
  • You want conversational flexibility — adjust categories, batch sizes, or audit accuracy on demand

When to switch approaches

  • You need instant triage (under 1 second) → use Zendesk native triggers
  • You want a visual workflow with additional actions (Slack, spreadsheet) → use n8n
  • You want zero AI cost and keyword matching is sufficient → use Zendesk native triggers

Common questions

How accurate is AI classification compared to keyword matching?

Claude Haiku typically achieves 90-95% accuracy on English-language support tickets, compared to 70-85% for keyword matching. The biggest gains are on vague tickets ("I need help with my order") and multilingual tickets where keywords don't apply.

Does this use Claude API credits?

Yes. Each ticket classification costs ~$0.001 with claude-haiku-4-5 (the cheapest model). 500 tickets/month = ~$0.50 in API costs. The Zendesk API calls are free.

Can I add custom categories?

Yes. Update the classification prompt in templates/classification-prompt.md to add, rename, or remove categories. The agent reads this template each time it runs, so changes take effect immediately.

Should I run this alongside Zendesk triggers or instead of them?

Both work. The most effective setup is triggers for obvious keyword matches (instant, free) and the Claude Code skill as a sweep to catch anything triggers missed. Run the skill every 30 minutes to triage the remaining untagged tickets.

Cost

  • Claude API — ~$0.001 per ticket classified with claude-haiku-4-5. 500 tickets/month = ~$0.50
  • Zendesk API — included in your plan, no per-call cost
  • Claude Code agent — $0.01-0.05 per skill invocation (reading files and generating code)
  • GitHub Actions (if scheduled) — free tier includes 2,000 minutes/month

Looking to scale your AI operations?

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