Alert Slack before Zendesk SLA breaches 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 SLA policies configured (Professional plan or above)
  • Slack bot with chat:write permission, added to the target channel
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
# Slack bot token starting with xoxb- (chat:write scope required)
SLACK_BOT_TOKEN=your_value_here
# Slack channel ID starting with C (right-click channel > View channel details)
SLACK_CHANNEL_ID=your_value_here

Why a Claude Code skill?

The other approaches in this guide are deterministic: they run the same logic every time, the same way. An Claude Code skill is different. You tell Claude what you want in plain language, and the skill gives it enough context to do it reliably.

That means you can say:

  • "Check for tickets approaching SLA breach and post warnings to Slack"
  • "Which urgent tickets are closest to breaching their first reply SLA?"
  • "Post an SLA health summary to #cx-leadership — how many tickets are at risk right now?"

The skill contains workflow guidelines, API reference materials, and a message 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 different warning threshold, filtering by priority or group, a summary instead of individual alerts — 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 (ticket search, metrics endpoint, response formats) so the agent calls the right APIs with the right parameters
  3. templates/ — a Slack Block Kit template so warning messages are consistently formatted 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 posted. 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, message 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 message formats.

Once installed, you can invoke a skill as a slash command (e.g., /zendesk-sla-watch), 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-sla-watch/{templates,references}

This creates the layout:

.claude/skills/zendesk-sla-watch/
├── SKILL.md                          # workflow guidelines + config
├── templates/
│   └── slack-alert.md                # Block Kit template for SLA warnings
└── references/
    └── zendesk-sla-api.md            # Zendesk ticket metrics API patterns

Step 2: Write the SKILL.md

Create .claude/skills/zendesk-sla-watch/SKILL.md:

---
name: zendesk-sla-watch
description: Check open Zendesk tickets for approaching SLA breaches and post warnings to Slack
disable-model-invocation: true
allowed-tools: Bash, Read
---
 
## Goal
 
Check all open Zendesk tickets for approaching SLA breaches (default: within 60 minutes of breach) and post a formatted warning per at-risk ticket to a Slack channel.
 
## 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)
- `SLACK_BOT_TOKEN` — Slack bot token starting with xoxb- (required)
- `SLACK_CHANNEL_ID` — Slack channel ID starting with C (required)
 
Default warning threshold: 60 minutes before breach. The user may request a different threshold.
 
## Workflow
 
1. Validate that all required env vars are set. If any are missing, print which ones and exit.
2. Search for all open tickets (status: open, pending, new) using the Zendesk Search API. See `references/zendesk-sla-api.md` for the endpoint and response format.
3. For each ticket, fetch SLA metrics via the ticket metrics endpoint. See `references/zendesk-sla-api.md` for the endpoint and response format.
4. For each SLA metric (first reply time, resolution time), check if the metric is not yet completed and the remaining time (target - elapsed) is within the warning threshold.
5. For each at-risk ticket, post a Block Kit warning to Slack using the format in `templates/slack-alert.md`.
6. Print a summary of how many warnings were posted.
 
## Important notes
 
- SLA metrics are in the `reply_time_in_minutes` and `full_resolution_time_in_minutes` fields. Each has `.business` and `.calendar` sub-objects. Use the `.business` sub-object for business-hours SLAs.
- Each sub-object contains `target` (total minutes allowed), `elapsed` (minutes used), and `is_completed` (boolean). Remaining = target - elapsed.
- Only check metrics where `is_completed` is false. Completed metrics have already met (or missed) their SLA.
- Authentication uses Basic Auth with `email/token:api_token` format. The `/token` suffix after the email is required.
- `SLACK_CHANNEL_ID` must be the channel ID (starts with `C`), not the channel name.
- The Slack bot must be invited to the target channel or `chat.postMessage` will fail with `not_in_channel`.
- Use only Python standard library (`urllib.request`, `json`, `base64`) — no pip dependencies needed.
- For large ticket volumes (500+), use the bulk endpoint `/api/v2/ticket_metrics/show_many.json` with comma-separated IDs instead of fetching one by one.

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-sla-api.md

Create .claude/skills/zendesk-sla-watch/references/zendesk-sla-api.md:

# Zendesk SLA & Ticket Metrics 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 open tickets
 
Find all tickets with active SLA timers.
 
**Request:**
 
```
GET https://{subdomain}.zendesk.com/api/v2/search.json?query=type:ticket status:open status:pending status:new&per_page=100
Authorization: Basic <credentials>
```
 
**Parameters:**
 
- `query` — URL-encoded search string. `type:ticket` limits to tickets. Multiple `status:` values are OR'd together.
- `per_page` — results per page (max 100). Use `next_page` URL from response to paginate.
 
**Response shape:**
 
```json
{
  "results": [
    {
      "id": 63104,
      "subject": "Unable to export reports since update",
      "priority": "high",
      "status": "open",
      "assignee_id": 11111,
      "requester_id": 67890
    }
  ],
  "count": 42,
  "next_page": "https://{subdomain}.zendesk.com/api/v2/search.json?page=2&..."
}
```
 
## Get ticket metrics (SLA data)
 
Fetch SLA timing data for a specific ticket.
 
**Request:**
 
```
GET https://{subdomain}.zendesk.com/api/v2/tickets/{ticket_id}/metrics.json
Authorization: Basic <credentials>
```
 
**Response shape:**
 
```json
{
  "ticket_metric": {
    "ticket_id": 63104,
    "reply_time_in_minutes": {
      "business": {
        "target": 120,
        "elapsed": 98,
        "is_completed": false
      },
      "calendar": {
        "target": 240,
        "elapsed": 195,
        "is_completed": false
      }
    },
    "full_resolution_time_in_minutes": {
      "business": {
        "target": 480,
        "elapsed": 320,
        "is_completed": false
      },
      "calendar": {
        "target": 960,
        "elapsed": 640,
        "is_completed": false
      }
    }
  }
}
```
 
Key fields:
- `reply_time_in_minutes` — first reply SLA metric
- `full_resolution_time_in_minutes` — resolution SLA metric
- `.business` — uses your Zendesk business hours schedule
- `.calendar` — uses 24/7 calendar hours
- `target` — total minutes allowed by the SLA policy
- `elapsed` — minutes consumed so far
- `is_completed` — true if the metric has been fulfilled (reply sent or ticket resolved)
 
**Remaining time calculation:** `remaining = target - elapsed`. If `remaining > 0` and `remaining <= warning_threshold` and `is_completed == false`, the ticket is approaching breach.
 
## Get user details (optional)
 
Resolve a user ID to a name for richer alerts.
 
**Request:**
 
```
GET https://{subdomain}.zendesk.com/api/v2/users/{user_id}.json
Authorization: Basic <credentials>
```
 
**Response shape:**
 
```json
{
  "user": {
    "id": 67890,
    "name": "Lisa Wang",
    "email": "lisa@acme.com"
  }
}
```
 
## Rate limits
 
Zendesk allows roughly 700 API requests per minute on Professional plans. This workflow makes 1 request per open ticket for metrics. With 200 tickets at 15-minute intervals, that's well under the limit. For 500+ tickets, use the bulk endpoint.
 
## Bulk metrics endpoint (for large volumes)
 
```
GET https://{subdomain}.zendesk.com/api/v2/ticket_metrics/show_many.json?ids=123,456,789
```
 
Accepts up to 100 comma-separated ticket metric IDs per request.

templates/slack-alert.md

Create .claude/skills/zendesk-sla-watch/templates/slack-alert.md:

# Slack SLA Warning Template
 
Use this Block Kit structure for each SLA breach warning.
 
## Block Kit JSON
 
```json
{
  "channel": "<SLACK_CHANNEL_ID>",
  "text": "SLA warning: #<ticket_id> — <metric> breach in <remaining> min",
  "blocks": [
    {
      "type": "header",
      "text": {
        "type": "plain_text",
        "text": "SLA Breach Warning",
        "emoji": true
      }
    },
    {
      "type": "section",
      "fields": [
        {"type": "mrkdwn", "text": "*Ticket:* <ticket_url|#ticket_id>"},
        {"type": "mrkdwn", "text": "*Subject:* <subject>"},
        {"type": "mrkdwn", "text": "*SLA Metric:* <metric_name>"},
        {"type": "mrkdwn", "text": "*Time Remaining:* <remaining> min"}
      ]
    },
    {
      "type": "section",
      "fields": [
        {"type": "mrkdwn", "text": "*Priority:* <priority>"},
        {"type": "mrkdwn", "text": "*Assignee:* <assignee>"}
      ]
    },
    {
      "type": "actions",
      "elements": [
        {
          "type": "button",
          "text": {"type": "plain_text", "text": "View in Zendesk"},
          "url": "<ticket_url>",
          "style": "danger"
        }
      ]
    }
  ]
}
```
 
## Notes
 
- The top-level `text` field is required by the Slack API as a fallback for notifications and accessibility. Always include it.
- The ticket URL format: `https://{subdomain}.zendesk.com/agent/tickets/{ticket_id}`
- Use the `danger` button style to make the "View in Zendesk" button red, visually signaling urgency.
- Priority should be the Zendesk priority value (urgent, high, normal, low).
- Assignee may be "Unassigned" if `assignee_id` is null — highlight this since unassigned tickets approaching breach need immediate routing.

Step 4: Test the skill

Invoke the skill conversationally:

/zendesk-sla-watch

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

Checking SLA status (warning threshold: 60 min)...
  Found 42 open tickets to check
  3 tickets approaching SLA breach:
  Posted: #63104 — Unable to export reports (First Reply Time: 22 min remaining)
  Posted: #63118 — API errors since update (First Reply Time: 51 min remaining)
  Posted: #63092 — Order stuck in processing (Resolution Time: 45 min remaining)
Done. Posted 3 warning(s) to Slack.

What the Slack alert looks like

What you'll get
#sla-warnings
SLA Monitorapp9:41 AM

SLA Breach Warning

Ticket: #63104 — Unable to export reports since update

SLA Metric: First Reply Time

Time Remaining: 22 min

Priority: High

Assignee: Unassigned

View in Zendesk

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

  • "Check for SLA breaches with a 2-hour warning threshold" — the agent adjusts the threshold
  • "Which urgent tickets are closest to breaching?" — the agent adds a priority filter
  • "Post an SLA health summary to #cx-leadership" — the agent adapts the output to a summary format
Test with a short SLA target

Create a test SLA policy with a 15-minute first reply target and assign it to a test ticket via a trigger condition (e.g., tag "sla-test"). This lets you see the full warning flow in minutes instead of waiting hours.

Step 5: Schedule it (optional)

Option A: Cron + Claude CLI

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

Option B: GitHub Actions + Claude

name: Zendesk SLA Watch
on:
  schedule:
    - cron: '*/15 13-22 * * 1-5'  # 8 AM-5 PM ET, weekdays
  workflow_dispatch: {}   # Manual trigger for testing
jobs:
  alert:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: anthropics/claude-code-action@v1
        with:
          prompt: "Run /zendesk-sla-watch"
          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 }}
          SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}
          SLACK_CHANNEL_ID: ${{ secrets.SLACK_CHANNEL_ID }}

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).

GitHub Actions cron uses UTC

*/15 13-22 * * 1-5 runs every 15 minutes from 1 PM to 10 PM UTC (8 AM to 5 PM ET). Adjust the hour range for your timezone. GitHub Actions cron may also have up to 15 minutes of delay.

Troubleshooting

When to use this approach

  • You want conversational flexibility — ad hoc queries like "which urgent tickets are closest to breaching?" alongside scheduled checks
  • You want on-demand SLA health checks during standups or escalation meetings
  • You're already using Claude Code and want skills that integrate with your workflow
  • You want to run tasks in the background via Claude Cowork while focusing on other work
  • You prefer guided references over rigid scripts — the agent adapts while staying reliable

When to switch approaches

  • You need 15-minute precision running 24/7 without LLM costs → use n8n
  • You want a zero-cost, zero-code setup within Zendesk → use the Zendesk Automation approach (accepts hourly variance)
  • You want a visual workflow builder → use n8n

Common questions

Why not just use a script?

A script runs the same way every time. The Claude Code skill adapts to what you ask — different warning thresholds, filtered by priority or agent group, summary format instead of individual alerts, a different channel. The reference files ensure it calls the right APIs even when improvising, so you get flexibility without sacrificing reliability.

Does this use Claude API credits?

Yes. Unlike a script-based approach, the agent reads skill files and generates code each time. Typical cost is $0.01-0.05 per invocation depending on how many tickets are checked. The Zendesk and Slack APIs themselves are free.

Can I run this on a schedule without a server?

Yes. GitHub Actions (Option B in Step 5) runs Claude on a cron schedule using GitHub's infrastructure. The free tier includes 2,000 minutes/month — more than enough for 15-minute intervals during business hours.

Will this hit Zendesk API rate limits?

Only if you have a very large open ticket queue. The skill makes 1 API call per open ticket for metrics. Zendesk allows ~700 requests/minute on Professional plans. Up to 200 open tickets is well within limits. For larger volumes, the reference file documents a bulk endpoint alternative.

Cost

  • Claude API — $0.01-0.05 per invocation (the agent reads files and generates code)
  • Zendesk API — included in Professional plan and above, no per-call cost
  • Slack API — included in all plans, no per-call cost
  • 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.