Swirls

SWIRLS_

  • How it works
  • Pricing

56 matches

OverviewDeliver client operations10Win and grow accounts8Resolve customer issues6Create and distribute content9Run people and operations5Review money and agreements3Monitor systems and data9Compose dependable work6

56 matches

OverviewDeliver client operations10Win and grow accounts8Resolve customer issues6Create and distribute content9Run people and operations5Review money and agreements3Monitor systems and data9Compose dependable work6
← Customer SupportWebhookView on GitHub

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.