Auto-close stale Gorgias tickets using n8n

medium complexityCost: $0-24/mo

Prerequisites

Prerequisites
  • n8n instance (cloud or self-hosted)
  • Gorgias account with REST API access
  • Gorgias API credentials: account email and API key

Overview

This workflow runs on a schedule, fetches Gorgias tickets that have been waiting on a customer reply for more than 48 hours, sends a polite follow-up message, and closes tickets where the follow-up was already sent and the customer still hasn't responded. Unlike Gorgias native Rules, n8n gives you full control over timing, conditions, and the ability to integrate external data (like checking a CRM before closing).

Step 1: Set up a Schedule trigger

Add a Schedule Trigger node:

  • Interval: Every 1 hour
  • Time zone: Your support team's primary time zone

Running hourly ensures tickets get closed reasonably close to the 48-hour mark without hammering the Gorgias API.

Step 2: Fetch pending tickets from Gorgias

Add an HTTP Request node to pull tickets waiting on a customer reply:

  • Method: GET
  • URL: https://your-store.gorgias.com/api/tickets
  • Authentication: Basic Auth (Gorgias email + API key)
  • Query parameters:
    • status: pending
    • limit: 50
{
  "method": "GET",
  "url": "https://your-store.gorgias.com/api/tickets",
  "authentication": "basicAuth",
  "qs": {
    "status": "pending",
    "limit": 50
  }
}

Step 3: Filter to stale tickets

Add a Code node to identify tickets that are past the 48-hour threshold:

const now = new Date();
const FOLLOW_UP_HOURS = 48;
const CLOSE_HOURS = 72; // 48h + 24h after follow-up
 
const results = [];
 
for (const item of $input.all()) {
  const ticket = item.json;
  const tickets = ticket.data || [ticket];
 
  for (const t of tickets) {
    const updatedAt = new Date(t.updated_datetime);
    const hoursSinceUpdate = (now - updatedAt) / (1000 * 60 * 60);
 
    // Check if last message is from an agent
    const messages = t.messages || [];
    const lastMessage = messages[messages.length - 1];
    const lastIsAgent = lastMessage?.source?.type === 'helpdesk';
 
    if (!lastIsAgent) continue;
 
    const tags = (t.tags || []).map(tag => tag.name || tag);
    const isVip = tags.includes('vip') || tags.includes('escalated');
    if (isVip) continue;
 
    const followUpSent = tags.includes('auto-close-sent');
 
    if (!followUpSent && hoursSinceUpdate >= FOLLOW_UP_HOURS) {
      results.push({
        json: {
          ticketId: t.id,
          action: 'send_follow_up',
          customerName: t.requester?.firstname || 'there',
          subject: t.subject,
          hoursSinceUpdate: Math.round(hoursSinceUpdate),
        }
      });
    } else if (followUpSent && hoursSinceUpdate >= CLOSE_HOURS) {
      results.push({
        json: {
          ticketId: t.id,
          action: 'close',
          subject: t.subject,
          hoursSinceUpdate: Math.round(hoursSinceUpdate),
        }
      });
    }
  }
}
 
return results;

Step 4: Route by action type

Add a Switch node to separate follow-up and close actions:

Value: {{ $json.action }}

CaseValueRoute to
1send_follow_upFollow-up message nodes
2closeClose ticket nodes

Step 5: Send the follow-up message

For the send_follow_up route, add an HTTP Request node:

  • Method: POST
  • URL: https://your-store.gorgias.com/api/tickets/{{ $json.ticketId }}/messages
  • Authentication: Basic Auth
  • Body (JSON):
{
  "body_text": "Hi {{ $json.customerName }},\n\nJust checking in — did our previous reply resolve your question? If you still need help, simply reply to this message and we'll pick things right back up.\n\nIf we don't hear from you, we'll close this ticket shortly to keep things tidy. You can always reach out again anytime.\n\nBest,\nThe Support Team",
  "channel": "email",
  "from_agent": true,
  "source": {
    "type": "helpdesk",
    "from": {
      "name": "Support Team",
      "address": "support@yourstore.com"
    }
  }
}

Then add a second HTTP Request to tag the ticket:

  • Method: PUT
  • URL: https://your-store.gorgias.com/api/tickets/{{ $json.ticketId }}
  • Body: { "tags": [{ "name": "auto-close-sent" }] }
Add a Wait node for precise timing

If you want exact 48-hour follow-ups rather than hourly batch processing, use a webhook trigger on ticket updates plus a Wait node set to 48 hours. The Wait node holds the execution and resumes after the delay. This is more precise but uses more n8n executions.

Step 6: Close the ticket

For the close route, add an HTTP Request node:

  • Method: PUT
  • URL: https://your-store.gorgias.com/api/tickets/{{ $json.ticketId }}
  • Body:
{
  "status": "closed",
  "tags": [
    { "name": "auto-closed" }
  ]
}

Step 7: Add error handling and notifications

Add Retry On Fail to all HTTP Request nodes (2 retries, 30-second delay).

Optionally, connect an Error Trigger node to a Slack or email notification so you know if the workflow fails:

{
  "text": "Auto-close workflow failed: {{ $json.error.message }}"
}
Watch for API rate limits

The Gorgias API has rate limits (typically 2 requests/second on standard plans). If you process many tickets per run, add a Wait node (1 second) between API calls to avoid hitting the limit.

Step 8: Activate and test

  1. Create a test ticket in Gorgias, reply as an agent, and manually set updated_datetime to 49 hours ago (or wait)
  2. Run the workflow manually and verify the follow-up message appears
  3. Wait for the close cycle and confirm the ticket closes
  4. Toggle the workflow to Active

Cost

  • n8n Cloud: ~4-6 node executions per processed ticket. 200 auto-closes/month = ~1,000 executions.
  • 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.