Zendesk CSAT Follow-Up and Save
When a Zendesk ticket is solved, the flow sends a CSAT survey, and on a low score creates a follow-up task, alerts a manager in Teams, and logs the detractor in Dataverse for a save play. Closes the loop on support quality and recovers unhappy customers.
Provided as-is, without warranty of any kind. Review and test each pattern in a non-production environment before deploying it to live automations. See our Terms.
Overview
This solution closes the loop on Zendesk support quality. It is built as two coordinated cloud flows in one solution:
1. Survey on Solve — when a ticket is solved, posts a CSAT survey link as a public comment and logs the send in a Dataverse tracking table with a correlation ID. 2. Low-Score Save Play — when a customer leaves a low (bad) CSAT rating, adds a private save-play note to the ticket, alerts a manager in Teams, and logs/updates the detractor in Dataverse for a recovery play.
Why it matters: Low-CSAT customers churn quietly. Detecting and acting on detractors quickly recovers relationships, and the Dataverse log gives leadership a governed record of detractors and save plays.
> Build note on the Zendesk model: The original draft referenced a numeric score / GetSatisfactionRating op. The real Zendesk connector is tabular (no SendSatisfaction/GetSatisfactionRating ops), and Zendesk satisfaction ratings use a categorical score of good / bad / offered / unoffered — not a 1–5 number. A detractor is therefore score = bad, not "≤ threshold". The solution is built to the real connector behaviour.
Use Case
A support team wants automatic CSAT collection on solved tickets and an automated save workflow that fires the moment a low rating arrives — without an agent having to watch for it.
Flow Architecture
Recurrence Check For Solved Tickets (Flow A)
Recurrence (15 min)Poll for solved tickets
Initialize Zendesk Subdomain / Auth Header / Survey Link Base (Flow A)
Initialize VariableLoad config from env vars
Get Solved Tickets (Flow A)
Zendesk GetItems (table tickets, $filter status eq 'solved')Read solved tickets
Apply to each Solved Ticket (Flow A)
ForeachProcess each ticket
Compose Correlation Id (Flow A)
Compose @guid()Mint trace ID stamped on the tracking row
Check If Already Surveyed (Flow A)
Dataverse ListRecordsIdempotency — no double survey
Condition Survey Not Yet Sent (Flow A)
If @empty(...)Only act on un-surveyed tickets
Send CSAT Survey Comment (Flow A)
HTTP PUT (Zendesk REST, public comment)Post the survey link onto the ticket
Log Survey Sent Record (Flow A)
Dataverse CreateRecordTrack the send + correlation ID
Recurrence Check For Low Scores (Flow B)
Recurrence (15 min)Poll for low ratings
Initialize Zendesk Subdomain / Auth Header / Low Score Value / Teams Group / Teams Channel / Manager Email (Flow B)
Environment Variables
| Schema name | Type | Default | Description |
|---|---|---|---|
| flowlibs_ZendeskSubdomain | String | contoso | Zendesk subdomain for REST URLs |
| flowlibs_ZendeskAuthHeader | String | Basic … (placeholder) | Authorization header for Zendesk REST (Basic base64(email/token:APITOKEN)) |
| flowlibs_CSATSurveyLinkBase | String | https://your-tenant.zendesk.com/hc/en-us/csat-survey?ticket= | Base URL appended with the ticket ID for the survey link |
| flowlibs_LowScoreValue | String | bad | Zendesk score that counts as a detractor |
| flowlibs_TeamsGroupId | String | <your-team-id> | Teams team/group for the manager alert |
| flowlibs_TeamsChannelId | String | <your-channel-id> | Teams channel for the manager alert |
| flowlibs_ManagerEmail | String | alerts@yourcompany.com | Manager referenced in the alert + private note |
Connectors & Connections
| Connector | API name | Actions used |
|---|---|---|
| Zendesk | shared_zendesk | GetItems |
| Microsoft Dataverse | shared_commondataserviceforapps | ListRecords CreateRecord UpdateRecord |
| Microsoft Teams | shared_teams | PostMessageToConversation |
Note — All connections are referenced as solution connection references; the flow is portable between environments as long as a connection is mapped at import time.
Customization Guide
Almost every realistic variant of this flow can be implemented by changing environment variable values. A few cases require small edits inside the flow definition — those are called out explicitly below.
- Real-time triggers
- Swap the 15-minute Recurrence for the Zendesk GetOnUpdatedItems batch trigger (tickets / satisfaction_ratings) for near-real-time response.
- Requester email
- Add a Zendesk GetItems on the users table (by requester_id) to store the real email instead of the requester id.
- Promoter play
- Branch on score = good to ask happy customers for a public review.
- Exec escalation
- Escalate to leadership for VIP accounts or repeat detractors.
- Root-cause tagging
- Capture the rating comment into a reason column for trend analysis / Power BI.
Key Expressions
The flow is intentionally light on Power Fx / WDL gymnastics — the heaviest expressions are the branch-name concatenation and the approval outcome check. They are listed below in the order they appear in the flow.
EXPR.01Detractor filter (Flow B)
OData filter for detractor ratings (default bad)
EXPR.02Idempotency lookup (Flow A)
Build the Dataverse filter to check for an existing survey row
EXPR.03Correlation reuse (Flow B)
Reuse Flow A's correlation id or mint a new one
EXPR.04Survey link
Build the per-ticket survey URL
EXPR.05Private note body (Flow B)
Compose the private save-play note text
Customize & download
Generate a ready-to-import copy of this solution with your environment-variable values baked in — available on Base, Pro, or Team.
Upgrade to customize
Comments
Sign in to join the conversation.
Sign inNo comments yet. Be the first to share your experience with this flow.