Alert your support lead in Slack when Gorgias tickets approach SLA deadline using n8n
Prerequisites
- 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
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.
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.
Need help implementing this?
We build and optimize automation systems for mid-market businesses. Let's discuss the right approach for your team.