CSAT Scorer
Scores customer satisfaction from support interactions and triggers follow-ups for unhappy customers.
aiswitchemail
Source
/**
* Scores customer satisfaction from support interactions
* and triggers follow-ups for unhappy customers.
*/
webhook support_interaction {
label: "Support Interaction"
schema: @json {
{
"type": "object",
"required": ["customer_email", "interaction_text", "channel"],
"properties": {
"customer_email": { "type": "string" },
"interaction_text": { "type": "string" },
"channel": { "type": "string", "enum": ["chat", "email", "phone"] },
"agent_name": { "type": "string" }
}
}
}
}
workflow score_csat {
label: "Score CSAT"
root {
type: code
label: "Extract interaction"
code: @ts { return context.nodes.root.input }
outputSchema: @json {
{
"type": "object",
"properties": {
"customer_email": { "type": "string" },
"interaction_text": { "type": "string" },
"channel": { "type": "string" },
"agent_name": { "type": "string" }
}
}
}
}
node score {
type: ai
label: "Score satisfaction"
kind: object
model: "google/gemini-2.5-flash"
prompt: @ts {
const interaction = context.nodes.root.output
return "Score the customer satisfaction of this " + interaction.channel + " support interaction from 1-5.\n\n" + interaction.interaction_text
}
schema: @json {
{
"type": "object",
"required": ["score", "resolved", "customer_sentiment"],
"properties": {
"score": { "type": "number", "minimum": 1, "maximum": 5 },
"resolved": { "type": "boolean" },
"customer_sentiment": { "type": "string", "enum": ["happy", "neutral", "frustrated", "angry"] },
"improvement_notes": { "type": "string" }
}
}
}
}
node route {
type: switch
label: "Route by score"
cases: ["needs_followup", "ok"]
router: @ts {
if (context.nodes.score.output.score <= 2) return "needs_followup"
return "ok"
}
}
node followup {
type: email
label: "Send follow-up"
from: @ts { return "[email protected]" }
to: @ts { return context.nodes.root.output.customer_email }
subject: @ts { return "Following up on your support experience" }
text: @ts {
return "We noticed your recent support experience may not have met your expectations. We are committed to doing better. A senior support specialist will reach out within 24 hours to ensure your issue is fully resolved."
}
}
node store_result {
type: code
label: "Store score"
code: @ts {
return {
customer: context.nodes.root.output.customer_email,
channel: context.nodes.root.output.channel,
score: context.nodes.score.output.score,
resolved: context.nodes.score.output.resolved,
sentiment: context.nodes.score.output.customer_sentiment
}
}
}
flow {
root -> score
score -> route
route -["needs_followup"]-> followup
route -["ok"]-> store_result
}
}
stream csat_scores {
label: "CSAT scores"
workflow: score_csat
version: v1
versions: {
v1 {
schema: @json {
{
"type": "object",
"required": ["customer", "channel", "score", "resolved", "sentiment"],
"properties": {
"customer": { "type": "string" },
"channel": { "type": "string" },
"score": { "type": "number" },
"resolved": { "type": "boolean" },
"sentiment": { "type": "string" }
}
}
}
condition: @ts { return true }
prepare: @ts {
const stored = context.output.store_result
if (stored) return stored
return {
customer: context.nodes.root.output.customer_email,
channel: context.nodes.root.output.channel,
score: context.nodes.score.output.score,
resolved: context.nodes.score.output.resolved,
sentiment: context.nodes.score.output.customer_sentiment
}
}
}
}
}
trigger on_interaction {
webhook:support_interaction -> score_csat
enabled: true
}
Flow
Trigger to workflow
support_interactionwebhook
score_csat
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.
Workflow nodes
CODE
Code Node
Extract interaction
AI
AI Node
Score satisfaction
SWITCH
Switch Node
Route by score
EMAIL
Email Node
Send follow-up
CODE
Code Node
Store score
Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.