Send Slack alerts for low Zendesk CSAT scores using n8n
Install this workflow
Download the n8n workflow JSON and import it into your n8n instance.
csat-alert.n8n.jsonPrerequisites
- n8n instance (cloud or self-hosted)
- Zendesk API credentials: subdomain, agent email, and API token (Admin Center → Apps and integrations → APIs → Zendesk API → enable Token Access)
- Slack Bot Token with
chat:writescope and the bot invited to your alert channel - Slack channel ID for your CSAT alerts channel (right-click channel → View channel details → copy ID at bottom)
Why n8n?
n8n is the best option for teams that want near-real-time CSAT alerts without writing or maintaining code. The visual workflow builder makes the polling logic transparent — you can see each step, test with real data, and modify the alert format without touching a script. Self-hosted n8n is free with unlimited executions; Cloud starts at $24/mo.
While Zendesk triggers can natively alert on bad ratings (Professional plan+), n8n adds rich Block Kit formatting, external context enrichment, and batch processing that native Slack notifications can't match. The trade-off is a polling delay (up to 5 minutes) vs. instant native triggers. For most teams, the richer alerts are worth the slight delay.
How it works
- Schedule Trigger runs the workflow every 5 minutes
- HTTP Request polls the Zendesk satisfaction ratings API filtered to
score=bad, sorted newest first - Code node filters to ratings submitted since the last poll cycle, preventing duplicate alerts
- HTTP Request fetches full ticket details (subject, requester, assignee) for each new bad rating
- HTTP Request posts a Block Kit message to Slack with ticket details, customer feedback, and a direct link to the Zendesk ticket
Step 1: Set up Zendesk credentials in n8n
Go to Credentials → Add credential → Zendesk API:
- Subdomain: your Zendesk subdomain (the
xxxpart ofxxx.zendesk.com) - Email: your agent email address
- API Token: the token from Admin Center → APIs → Zendesk API
Test the connection to verify access.
Step 2: Add a Schedule Trigger node
Add a Schedule Trigger node as the workflow entry point:
- Interval: Every 5 minutes
This is the polling frequency. Five minutes balances responsiveness with API rate limits.
Zendesk allows roughly 700 API requests per minute. This workflow uses 2-3 requests per poll cycle (1 for ratings, 1 per bad rating for ticket details). At 5-minute intervals, that's well under 1% of your rate limit — leaving plenty of headroom for other integrations.
Step 3: Poll the Zendesk satisfaction ratings API
Add an HTTP Request node:
- Method: GET
- URL:
https://YOUR_SUBDOMAIN.zendesk.com/api/v2/satisfaction_ratings?score=bad&sort_order=desc&per_page=10 - Authentication: Predefined Credential Type → Zendesk API (or use Basic Auth with
email/token:api_token)
The score=bad parameter filters to only unsatisfied ratings. The sort_order=desc returns newest first so you only need the first page.
Step 4: Filter to new ratings only
Add a Code node to deduplicate. Since we poll every 5 minutes, we only want ratings that appeared since the last poll:
const ratings = $input.first().json.satisfaction_ratings || [];
const fiveMinAgo = new Date(Date.now() - 5 * 60 * 1000).toISOString();
const newBadRatings = ratings.filter(r => r.updated_at > fiveMinAgo);
if (newBadRatings.length === 0) {
return []; // No new bad ratings — stop workflow
}
return newBadRatings.map(r => ({
json: {
ratingId: r.id,
ticketId: r.ticket_id,
comment: r.comment || '(no comment)',
createdAt: r.updated_at,
}
}));If no new bad ratings exist, the workflow stops here — no unnecessary Slack or Zendesk API calls.
If a poll cycle takes longer than expected or n8n skips an interval, you could miss ratings. A safer approach is to store the last-seen rating ID in n8n's static data and filter by ID instead of time. For most teams, the 5-minute time window is reliable enough.
Step 5: Fetch ticket details
Add an HTTP Request node to get context for each bad rating:
- Method: GET
- URL:
https://YOUR_SUBDOMAIN.zendesk.com/api/v2/tickets/{'{'}$json.ticketId{'}'}.json - Authentication: Same Zendesk credentials
- Batching: Process items individually (one request per bad rating)
This returns the ticket subject, requester name, assignee, tags, and other details you'll include in the Slack alert.
Step 6: Post a Slack Block Kit alert
Add an HTTP Request node (or Slack node) to post the alert:
- Method: POST
- URL:
https://slack.com/api/chat.postMessage - Headers:
Authorization: Bearer YOUR_SLACK_BOT_TOKEN - Body (JSON):
{
"channel": "CSAT_CHANNEL_ID",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "Bad CSAT Rating Received",
"emoji": true
}
},
{
"type": "section",
"fields": [
{"type": "mrkdwn", "text": "*Ticket:* #{{$json.ticketId}}"},
{"type": "mrkdwn", "text": "*Subject:* {{$json.ticket.subject}}"},
{"type": "mrkdwn", "text": "*Customer:* {{$json.ticket.requester.name}}"},
{"type": "mrkdwn", "text": "*Assignee:* {{$json.ticket.assignee_id}}"}
]
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Customer comment:* {{$json.comment}}"
}
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {"type": "plain_text", "text": "View in Zendesk"},
"url": "https://YOUR_SUBDOMAIN.zendesk.com/agent/tickets/{{$json.ticketId}}"
}
]
}
]
}Replace YOUR_SUBDOMAIN with your Zendesk subdomain and CSAT_CHANNEL_ID with your Slack channel ID.
When a customer rates "Bad," Zendesk optionally lets them leave a comment explaining why. Including this in the Slack alert gives the follow-up agent immediate context about what went wrong, without needing to open the ticket first.
Step 7: Reopen the ticket (optional)
If your team wants bad-rated tickets automatically reopened for follow-up, add another HTTP Request node:
- Method: PUT
- URL:
https://YOUR_SUBDOMAIN.zendesk.com/api/v2/tickets/{'{'}$json.ticketId{'}'}.json - Body:
{
"ticket": {
"status": "open",
"tags": ["csat-bad", "needs-followup"]
}
}This reopens the ticket and adds tags so agents can filter to all bad-CSAT tickets in a Zendesk View.
Step 8: Activate and test
- Set the workflow to Active
- To test immediately, submit a bad CSAT rating on a solved ticket in your Zendesk sandbox
- Wait for the next 5-minute poll cycle (or trigger the workflow manually)
- Verify the Slack alert appears with the correct ticket details and link
- Enable Retry On Fail on all HTTP Request nodes for resilience against temporary API errors
You must poll the API on a schedule. Polling every 5 minutes balances responsiveness with API rate limits. Zendesk allows roughly 700 requests per minute on Professional plans. Do not poll more frequently than every 2 minutes — it wastes executions and provides minimal benefit since customers rarely submit ratings in real-time.
Troubleshooting
Common questions
Will this hit Zendesk API rate limits?
No. Zendesk allows roughly 700 requests per minute on Professional plans. This workflow makes 1 request per poll cycle (the ratings endpoint), plus 1 per bad rating for ticket details. Even with 10 bad ratings per cycle at 5-minute intervals, that's well under 1% of your rate limit.
How do I prevent duplicate alerts for the same bad rating?
The Code node filters by a 5-minute time window. For more robust deduplication, store the last-seen rating ID in n8n's static data ($getWorkflowStaticData('global').lastSeenId) and filter by ID instead of time. This eliminates gaps if a poll cycle is delayed or skipped.
Should I use n8n Cloud or self-hosted for this?
For a single workflow like this, self-hosted is free and straightforward — a $5/mo VPS handles it. n8n Cloud ($24/mo starter) makes sense if you're running multiple workflows and don't want to manage infrastructure. The workflow is identical on both.
Can I also reopen tickets with bad ratings automatically?
Yes. Step 7 in this guide shows how to add an HTTP Request node that reopens the ticket and adds a csat-bad tag. This creates a follow-up queue your team can work through. Skip this if you prefer manual triage.
Cost
- n8n Cloud Starter: $0-24/mo depending on execution volume. This workflow uses approximately 4 node executions per poll cycle. At 5-minute intervals, that's ~1,152 executions per day — well within the Starter plan limits.
- n8n self-hosted: Free. Only costs are your hosting infrastructure.
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.