Alert your support lead in Slack when Gorgias tickets approach SLA deadline using n8n
Install this workflow
Download the n8n workflow JSON and import it into your n8n instance.
sla-breach-alert.n8n.jsonPrerequisites
- n8n instance (cloud or self-hosted)
- Gorgias account with REST API access and API credentials (email + API key)
- Slack app with Bot Token and
chat:writescope - A Slack channel for SLA warnings (e.g.,
#support-sla-warnings) - Your SLA targets defined: first-response time per channel or priority level
Why n8n?
n8n is the best option for proactive SLA alerting because Gorgias cannot natively send Slack alerts on SLA deadlines. SLA risk is a function of elapsed time, not a discrete event — so you need a scheduled workflow that polls open tickets, calculates time remaining, and alerts when tickets cross your warning threshold.
Self-hosted n8n is completely free with unlimited executions. n8n Cloud starts at $24/mo. The workflow runs every 15 minutes regardless of ticket volume, so execution costs are predictable — roughly 576 executions/day on a 15-minute interval.
How it works
- Schedule Trigger fires every 15 minutes (configurable — 5 minutes for aggressive SLAs)
- HTTP Request node fetches all open tickets from the Gorgias API
- Code node filters to tickets with no agent reply and calculates SLA time remaining based on per-channel targets
- Code node deduplicates alerts using n8n static data (1-hour cooldown per ticket)
- Slack node posts a Block Kit alert with customer name, channel, time remaining, SLA target, and a direct link
- Optional batch node combines multiple at-risk tickets into a single Slack message to reduce noise
Overview
This workflow runs on a schedule (every 15 minutes), fetches all open Gorgias tickets that haven't received a first response, calculates how much SLA time remains for each, and posts a Slack alert for any ticket that has crossed your warning threshold. The lead gets actionable context — ticket subject, customer name, time remaining, and a direct link — so they can reassign or escalate before the SLA actually breaches.
Step 1: Create the Schedule trigger
Add a Schedule Trigger node:
- Interval: Every 15 minutes
- Timezone: Your support team's primary timezone
15 minutes is a good default. For aggressive SLAs (under 1 hour), consider running every 5 minutes.
Gorgias webhooks fire on ticket creation and updates, but not on SLA deadlines approaching. Since SLA risk is a function of elapsed time (not a discrete event), polling on a schedule is the right pattern.
Step 2: Fetch open tickets awaiting first response
Add an HTTP Request node:
- Method: GET
- URL:
https://{{ $vars.GORGIAS_DOMAIN }}.gorgias.com/api/tickets - Authentication: Basic Auth (Gorgias email + API key)
- Query Parameters:
status:openlimit:100
Then add a Code node to filter to tickets with no agent reply:
const tickets = $input.first().json.data || [];
const noReply = tickets.filter(t => {
const messages = t.messages || [];
// Check if any message is from an agent (not the customer)
const hasAgentReply = messages.some(
m => m.source?.type === 'internal' || m.sender?.type === 'agent'
);
return !hasAgentReply;
});
return noReply.map(t => ({ json: t }));Step 3: Calculate SLA time remaining
Add a Code node to compute how much time each ticket has left:
// Define SLA targets in minutes per channel
const SLA_TARGETS = {
email: 240, // 4 hours
chat: 15, // 15 minutes
contact_form: 240,
default: 240,
};
// Warning threshold: alert when this percentage of SLA has elapsed
const WARN_THRESHOLD = 0.75;
const now = new Date();
const results = [];
for (const item of $input.all()) {
const ticket = item.json;
const channel = ticket.channel || 'default';
const slaMinutes = SLA_TARGETS[channel] || SLA_TARGETS.default;
const created = new Date(ticket.created_datetime);
const elapsedMs = now - created;
const elapsedMinutes = elapsedMs / 60000;
const remainingMinutes = slaMinutes - elapsedMinutes;
const percentElapsed = elapsedMinutes / slaMinutes;
if (percentElapsed >= WARN_THRESHOLD) {
results.push({
json: {
ticketId: ticket.id,
subject: ticket.subject || '(no subject)',
customerName: ticket.requester?.name || ticket.requester?.email || 'Unknown',
customerEmail: ticket.requester?.email || '',
channel,
slaMinutes,
elapsedMinutes: Math.round(elapsedMinutes),
remainingMinutes: Math.round(Math.max(remainingMinutes, 0)),
breached: remainingMinutes <= 0,
percentElapsed: Math.round(percentElapsed * 100),
}
});
}
}
return results;The calculation above uses wall-clock time. If your SLA policy counts only business hours, you'll need to adjust the elapsed time calculation to exclude nights and weekends. Add a helper function that subtracts non-business hours from the elapsed duration, or store your business-hours schedule in a config node.
Step 4: Deduplicate alerts
Add a Code node to avoid re-alerting on the same ticket within a short window. Use n8n's static data to track recently alerted ticket IDs:
const staticData = $getWorkflowStaticData('global');
if (!staticData.alertedTickets) {
staticData.alertedTickets = {};
}
const now = Date.now();
const COOLDOWN_MS = 60 * 60 * 1000; // 1 hour cooldown
// Clean up old entries
for (const [id, timestamp] of Object.entries(staticData.alertedTickets)) {
if (now - timestamp > COOLDOWN_MS) {
delete staticData.alertedTickets[id];
}
}
// Filter to tickets not recently alerted
const newAlerts = [];
for (const item of $input.all()) {
const ticketId = String(item.json.ticketId);
if (!staticData.alertedTickets[ticketId]) {
staticData.alertedTickets[ticketId] = now;
newAlerts.push(item);
}
}
return newAlerts;Step 5: Post the Slack alert
Add a Slack node:
- Resource: Message
- Operation: Send a Message
- Channel:
#support-sla-warnings - Message Type: Block Kit
{
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "⏱️ SLA Warning — Ticket Approaching Deadline"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*Customer*\n{{ $json.customerName }}"
},
{
"type": "mrkdwn",
"text": "*Channel*\n{{ $json.channel }}"
},
{
"type": "mrkdwn",
"text": "*Time Remaining*\n{{ $json.breached ? '❌ BREACHED' : $json.remainingMinutes + ' min' }}"
},
{
"type": "mrkdwn",
"text": "*SLA Target*\n{{ $json.slaMinutes }} min"
}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Subject*\n{{ $json.subject }}"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": { "type": "plain_text", "text": "Open in Gorgias" },
"url": "https://{{ $vars.GORGIAS_DOMAIN }}.gorgias.com/app/ticket/{{ $json.ticketId }}",
"style": "danger"
}
]
}
]
}Step 6: Batch multiple warnings (optional)
If your queue regularly has multiple at-risk tickets, posting one message per ticket creates noise. Add a Code node before the Slack node to batch all warnings into a single message:
const items = $input.all();
if (items.length === 0) return [];
const lines = items.map(item => {
const t = item.json;
const status = t.breached ? '❌ BREACHED' : `⏱️ ${t.remainingMinutes} min left`;
return `• *<https://your-store.gorgias.com/app/ticket/${t.ticketId}|#${t.ticketId}>* — ${t.subject} (${t.customerName}) — ${status}`;
});
return [{
json: {
summary: `${items.length} ticket(s) approaching SLA deadline`,
details: lines.join('\n'),
}
}];Then update the Slack node to post {{ $json.summary }} as the header and {{ $json.details }} as the body.
Step 7: Activate and test
- Set one of your SLA targets temporarily low (e.g., 5 minutes) for testing
- Create a test ticket and wait for it to age past the warning threshold
- Verify the Slack alert appears with correct time-remaining data
- Reset your SLA targets and toggle the workflow to Active
If you're seeing more than 10 SLA warnings per day, either your SLA targets are too tight for your current staffing level or your warning threshold is too aggressive. Adjust the WARN_THRESHOLD from 0.75 to 0.85 to reduce noise while still giving your team lead time.
Common questions
Should I use business hours or calendar time for SLA calculations?
Business hours if your team doesn't work nights and weekends. The Code node above uses wall-clock time — to switch to business hours, add a helper function that subtracts non-business hours from the elapsed duration. Without this adjustment, tickets created Friday at 5 PM will show as breached by Monday morning.
How do I avoid re-alerting on the same ticket every 15 minutes?
The deduplication Code node in Step 4 uses n8n static data to track recently alerted ticket IDs with a 1-hour cooldown. A ticket is only alerted once per hour. Adjust the COOLDOWN_MS constant if you want a longer or shorter cooldown.
What warning threshold should I use?
Start with 75% of your SLA window (the default WARN_THRESHOLD of 0.75). For a 4-hour SLA, this alerts at the 3-hour mark, giving your team 1 hour to act. If you're getting more than 10 alerts per day, raise the threshold to 0.85 to reduce noise.
Cost
- n8n Cloud: ~6 node executions per poll (every 15 minutes = ~576 executions/day). Well within the free tier for light usage; small teams will stay under the Starter plan limits.
- Self-hosted: Free.
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.