Alert Slack before Zendesk SLA breaches using n8n
Install this workflow
Download the n8n workflow JSON and import it into your n8n instance.
sla-breach.n8n.jsonPrerequisites
- n8n instance (cloud or self-hosted)
- Zendesk API credentials: agent email and API token (Admin Center → Apps and integrations → APIs → Zendesk API)
- Your Zendesk subdomain (the
acmepart ofacme.zendesk.com) - Slack Bot Token with
chat:writescope - Slack channel ID for SLA alerts (right-click channel → View channel details → copy the ID at the bottom)
Why n8n?
n8n is the best option for teams that need tighter SLA monitoring than Zendesk automations can provide. The visual workflow builder makes the polling and filtering logic transparent — you can see each step, test with real tickets, and adjust thresholds without writing a script. Self-hosted n8n is free with unlimited executions; Cloud starts at $24/mo.
The main advantage over native Zendesk automations is timing precision. This workflow runs every 15 minutes instead of Zendesk's hourly batch cycle with up to 1-hour variance. For a 2-hour first reply SLA, that's the difference between a 60-minute warning and a 15-minute warning. The trade-off is complexity — you're managing an external workflow instead of a built-in automation.
How it works
- Schedule Trigger runs the workflow every 15 minutes
- HTTP Request searches for all open, pending, and new tickets via the Zendesk Search API
- SplitInBatches + HTTP Request fetches SLA metrics (
/tickets/{id}/metrics.json) for each ticket - Code node calculates remaining time for first reply and resolution SLAs, filters to tickets within the warning threshold
- HTTP Request posts a Block Kit message to Slack with ticket details, SLA metric, time remaining, and a direct link
Step 1: Set up Zendesk credentials in n8n
Create a new credential in n8n:
- Type: Zendesk API
- Subdomain: your Zendesk subdomain (e.g.,
acme) - Email: your Zendesk agent email
- API Token: your Zendesk API token
Zendesk API auth uses the format {'{'}email{'}'}/token:{'{'}api_token{'}'} as the username with Basic auth. n8n handles this automatically when you use the Zendesk credential type.
Step 2: Add a Schedule Trigger
Add a Schedule Trigger node as the workflow entry point:
- Trigger Interval: Every 15 minutes
This gives you four checks per hour — enough to catch most approaching breaches with time for agents to act.
Step 3: Fetch open tickets
Add an HTTP Request node to search for open, pending, and new tickets:
- Method: GET
- URL:
https://{'{'}your-subdomain{'}'}.zendesk.com/api/v2/search.json?query=type:ticket status:open status:pending status:new&per_page=100 - Authentication: Predefined Credential Type → Zendesk API
- Credential: Select your Zendesk credential
This returns all active tickets that could have SLA timers running.
The Zendesk search API returns up to 100 results per page. If you have more than 100 open tickets, add a Loop node to paginate using the next_page URL from the response. For most teams, 100 results captures the active queue.
Step 4: Loop through tickets to get metrics
Add a SplitInBatches node to iterate over each ticket from the search results.
Then add an HTTP Request node inside the loop to fetch SLA metrics for each ticket:
- Method: GET
- URL:
https://{'{'}your-subdomain{'}'}.zendesk.com/api/v2/tickets/{'{'}$json.id{'}'}/metrics.json - Authentication: Predefined Credential Type → Zendesk API
- Credential: Select your Zendesk credential
This endpoint returns the ticket's SLA timing data including reply_time_in_minutes and full_resolution_time_in_minutes, each with business and calendar sub-objects containing target, elapsed, and is_completed fields.
Step 5: Merge and filter for approaching breaches
After the loop completes, add a Code node to check which tickets are approaching their SLA deadline:
const metrics = $input.all();
const warnings = [];
const WARN_MINUTES = 60; // Alert 60 minutes before breach
for (const item of metrics) {
const m = item.json.ticket_metric;
if (!m) continue;
const ticketId = m.ticket_id;
// Check reply time SLA
const replyBreach = m.reply_time_in_minutes?.business;
if (replyBreach && !replyBreach.is_completed) {
const target = replyBreach.target;
const elapsed = replyBreach.elapsed;
const remaining = target - elapsed;
if (remaining > 0 && remaining <= WARN_MINUTES) {
warnings.push({
ticketId,
metric: 'First Reply Time',
remainingMinutes: remaining,
target,
});
}
}
// Check resolution time SLA
const resolutionBreach = m.full_resolution_time_in_minutes?.business;
if (resolutionBreach && !resolutionBreach.is_completed) {
const target = resolutionBreach.target;
const elapsed = resolutionBreach.elapsed;
const remaining = target - elapsed;
if (remaining > 0 && remaining <= WARN_MINUTES) {
warnings.push({
ticketId,
metric: 'Resolution Time',
remainingMinutes: remaining,
target,
});
}
}
}
if (warnings.length === 0) return [];
return warnings.map(w => ({ json: w }));This node outputs only the tickets that are within 60 minutes of breaching their SLA. If no tickets are at risk, the workflow stops here.
60 minutes works for most teams, but high-priority tickets with tight SLAs might need 120 minutes of lead time. You can also make this dynamic — check the ticket priority and use a longer threshold for urgent tickets.
Step 6: Enrich with ticket details
Add another HTTP Request node to fetch the full ticket details for each warning (subject, requester, assignee):
- Method: GET
- URL:
https://{'{'}your-subdomain{'}'}.zendesk.com/api/v2/tickets/{'{'}$json.ticketId{'}'}.json - Authentication: Predefined Credential Type → Zendesk API
Merge the ticket subject, assignee name, and priority into the warning object using a Set node or a short Code node.
Step 7: Send Slack Block Kit alerts
Add an HTTP Request node to post the Slack message:
- Method: POST
- URL:
https://slack.com/api/chat.postMessage - Authentication: Header Auth
- Header Name: Authorization
- Header Value:
Bearer YOUR_SLACK_BOT_TOKEN - Body Content Type: JSON
- Body:
{
"channel": "SLA_CHANNEL_ID",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "SLA Breach Warning"
}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": "*Ticket:* #{{ $json.ticketId }}"},
{"type": "mrkdwn", "text": "*Subject:* {{ $json.subject }}"},
{"type": "mrkdwn", "text": "*SLA Metric:* {{ $json.metric }}"},
{"type": "mrkdwn", "text": "*Time Remaining:* {{ $json.remainingMinutes }} min"}
]
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": "*Assignee:* {{ $json.assignee }}"},
{"type": "mrkdwn", "text": "*Priority:* {{ $json.priority }}"}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "View Ticket"},
"url": "https://{{ $json.subdomain }}.zendesk.com/agent/tickets/{{ $json.ticketId }}",
"style": "danger"
}
]
}
]
}Replace SLA_CHANNEL_ID with your Slack channel ID and YOUR_SLACK_BOT_TOKEN with your bot token (or use n8n credentials).
Step 8: Activate and test
- Set a temporary SLA policy with a short target (e.g., 15-minute first reply) for testing
- Create a test ticket and leave it unanswered
- Run the workflow manually and verify the Slack alert appears
- Check that the Block Kit message renders correctly with the ticket link, time remaining, and assignee
- Toggle the workflow to Active
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.
This workflow makes one API call per ticket to fetch metrics. If you have 500+ open tickets, this can approach Zendesk's rate limit of roughly 700 requests per minute. For large volumes, batch the ticket IDs and use the GET /api/v2/ticket_metrics/show_many endpoint to fetch metrics in bulk instead of one by one.
Troubleshooting
Common questions
Will this hit Zendesk API rate limits with a large ticket queue?
Possibly. This workflow makes 1 API call per open ticket per run to fetch metrics. With 500+ open tickets at 15-minute intervals, you'll approach Zendesk's ~700 requests/minute limit. For large queues, use the bulk endpoint GET /api/v2/ticket_metrics/show_many.json with comma-separated ticket IDs to fetch metrics in batches of 100 instead of one by one.
How do I prevent duplicate warnings for the same ticket?
By default, a ticket within the warning window triggers a new alert every 15 minutes. Use n8n's static data ($getWorkflowStaticData('global')) to store warned ticket IDs and skip any ticket already warned in the current breach window. Clear the list daily or when the breach window passes.
Should I use n8n Cloud or self-hosted?
For a single workflow, self-hosted on a $5/mo VPS is free and handles the load. n8n Cloud ($24/mo starter) makes sense if you run multiple workflows and want managed infrastructure. The workflow is identical on both.
What if my SLA uses business hours and the metrics seem wrong?
The ticket_metric endpoint returns both .business and .calendar sub-objects. Business hours calculations depend on your Zendesk schedule configuration (Admin Center > Schedules). If the remaining time seems off, check that your business hours schedule accounts for your timezone, weekends, and holidays.
Cost
- n8n Cloud Starter: $0-24/mo depending on execution volume. Each run triggers ~3 node executions per ticket (search + metrics + Slack). 50 open tickets checked every 15 minutes = roughly 9,000 executions/month.
- n8n self-hosted: Free. You provide the infrastructure.
- Zendesk API: Included in your plan. Rate limits apply but are generous for typical ticket volumes.
- Slack API: Free for posting messages.
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.