Implementing Closed-Loop Alert Systems that Notify Managers of Critical Survey Detractor Scores

Implementing Closed-Loop Alert Systems that Notify Managers of Critical Survey Detractor Scores

What This Guide Covers

This guide details the architectural implementation of a real-time closed-loop alert system in Genesys Cloud CX that triggers immediate notifications to supervisors when a customer submits a survey score below a defined threshold. The end result is an automated workflow that parses survey responses, identifies detractors, and pushes actionable context to managers via Teams, Slack, or email within seconds of submission, enabling rapid intervention.

Prerequisites, Roles & Licensing

  • Licensing: Genesys Cloud CX 3 (required for full Architect capabilities and API access). Customer Experience (CX) analytics licenses are required for the agents receiving the surveys.
  • Permissions:
    • Architect > Architect flow > Read/Write
    • Survey > Survey > Read/Write
    • Integration > Integration > Read/Write (for Teams/Slack connectors)
    • User > User > Read (to resolve manager hierarchy if dynamic)
  • External Dependencies:
    • A configured Survey Flow in Architect.
    • An active Microsoft Teams or Slack workspace with a Genesys Cloud Integration installed.
    • A Microsoft 365 or Slack Admin account to grant necessary API permissions to the Genesys integration app.
  • OAuth Scopes: If building a custom webhooks endpoint instead of using native integrations, you will need survey:read and user:read scopes on your client credentials.

The Implementation Deep-Dive

1. Configuring the Survey Flow to Emit Structured Data

The foundation of a reliable closed-loop system is the data structure exiting the survey. Many implementations fail because they treat survey responses as opaque strings rather than structured key-value pairs. You must ensure your survey flow captures the score and the interaction context in a format that Architect can process downstream.

In your Survey Flow, ensure the final step before the End block captures the overall satisfaction score. Use a Set Variable block to store the score in a variable named overall_score. Crucially, you must also capture the interaction.id and agent.id (if applicable) into variables. These identifiers are non-negotiable for the manager to trace the interaction back to the recording or transcription.

The Trap: Failing to handle null or skipped responses. If a customer abandons the survey halfway through, the overall_score variable may be null or undefined. If your downstream logic assumes an integer, this causes a runtime error in the alerting flow, resulting in silent failures where no alert is generated, and managers remain unaware of the potential issue.

Architectural Reasoning: We capture the interaction.id at the survey level because survey responses are decoupled from the interaction object in the Genesys data model. The survey object contains a reference to the interaction, but passing the ID explicitly in the variable set ensures that if the interaction link is broken or delayed in propagation, your alert still has the precise reference key needed to query the interaction history API later.

Configure the Send Email/SMS/Teams block or the API Request block at the end of the survey flow to trigger the alert. However, a more robust pattern is to use the Survey Response Webhook or a Flow triggered by Survey Completion. For this guide, we will use the Architect Flow triggered by Survey Completion pattern, which allows for complex logic branching before notification.

2. Building the Alerting Flow with Conditional Logic

Create a new Architect Flow named Flow: Detractor Alert. Set the trigger to Survey Response. This trigger fires immediately after a survey is completed.

Step 2.1: Ingesting and Validating the Score

The first block in the flow should be a Set Variable block to extract the relevant data from the trigger payload.

  • Set score to trigger.data.score (or the specific field name from your survey configuration, e.g., trigger.data.overall_satisfaction).
  • Set interaction_id to trigger.data.interaction.id.
  • Set customer_email to trigger.data.customer.email.

Next, add a Split block. This block evaluates the score.

  • Condition: score <= 6 (assuming a 1-10 NPS scale where 0-6 are detractors).
  • True Branch: Proceed to alerting.
  • False Branch: End.

The Trap: Using a static threshold without considering the survey type. If you reuse this flow for CSAT (1-5 scale) and NPS (0-10 scale), a score of 6 is a detractor in NPS but a promoter in CSAT. Hardcoding score <= 6 breaks when you add CSAT surveys.
Solution: Add a second condition in the Split block checking the survey type or the maximum possible score. Alternatively, use a Set Variable block to define a threshold variable earlier, pulled from a Data Store or Configuration, allowing non-technical users to adjust the sensitivity without modifying the flow logic.

Step 2.2: Resolving the Manager Recipient

Sending alerts to a generic “support team” channel dilutes accountability. The goal is to notify the specific manager responsible for the agent involved.

Add a Data Store Lookup block to find the manager. You must maintain a Data Store (e.g., ManagerHierarchy) that maps agent_id to manager_email or manager_user_id.

  • Key: agent_id (from the trigger or interaction metadata).
  • Value: manager_email.

If you do not have a Data Store, you can use an API Request block to query the Genesys Cloud User API.

  • Endpoint: GET /api/v2/users
  • Query Params: id={agent_id}
  • Parse the response to find the managerId field.
  • Perform a second API Request to get the manager’s details: GET /api/v2/users/{managerId} to retrieve their email or Teams ID.

The Trap: Infinite loops or performance degradation from synchronous API calls in high-volume flows. Querying the User API for every single detractor response adds latency. If you have 1,000 detractors an hour, 1,000 API calls can hit rate limits or slow down the flow execution, causing timeouts.
Solution: Cache the manager mappings. Use the Data Store approach with a TTL (Time To Live) of 24 hours. Update the Data Store via a separate scheduled flow that runs nightly to sync user hierarchy changes. This decouples the read-heavy alerting flow from the write-heavy user management operations.

Step 2.3: Constructing the Alert Payload

Now that you have the recipient, construct the notification. We recommend using the Send Teams Message or Send Slack Message block in Architect. These native blocks are more reliable than webhook-based integrations because they handle OAuth token refresh and connection pooling automatically.

Configure the message body to include actionable links. Do not just send the score. Send the context.

JSON Payload Structure for Custom Webhooks (if not using native blocks):
If you are using a custom API Request to a middleware like Zapier or Make, or a custom endpoint, your JSON body should look like this:

{
  "recipient": "{{manager_email}}",
  "subject": "CRITICAL: Detractor Score Alert",
  "body": "Agent {{agent_name}} received a score of {{score}}/10. \n\nInteraction ID: {{interaction_id}}\nCustomer: {{customer_email}}\n\n[View Interaction](https://{{env_url}}/interactions/{{interaction_id}})\n[View Agent Profile](https://{{env_url}}/users/{{agent_id}})",
  "priority": "high"
}

The Trap: Including sensitive PII (Personally Identifiable Information) in public channels. If you send the alert to a general Teams channel, you might violate GDPR or CCPA by exposing customer emails or phone numbers.
Solution: Only send alerts to private channels or direct messages to the manager. If using a general channel, redact PII in the message body. Use the Replace function in Architect to mask email addresses: replace(customer_email, "@", "[at]").

3. Integrating with Microsoft Teams for Rich Notifications

To elevate the alert from a simple text message to a rich, actionable card, use the Adaptive Cards capability within the Teams integration.

In the Send Teams Message block, select Adaptive Card as the message type.

Adaptive Card JSON Example:

{
  "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
  "type": "AdaptiveCard",
  "version": "1.2",
  "body": [
    {
      "type": "TextBlock",
      "text": "🚨 Detractor Alert",
      "weight": "Bolder",
      "size": "Large",
      "color": "Attention"
    },
    {
      "type": "FactSet",
      "facts": [
        {
          "title": "Score:",
          "value": "{{score}}"
        },
        {
          "title": "Agent:",
          "value": "{{agent_name}}"
        },
        {
          "title": "Interaction ID:",
          "value": "{{interaction_id}}"
        }
      ]
    }
  ],
  "actions": [
    {
      "type": "Action.OpenUrl",
      "title": "Review Interaction",
      "url": "https://{{env_url}}/interactions/{{interaction_id}}"
    },
    {
      "type": "Action.OpenUrl",
      "title": "Listen to Recording",
      "url": "https://{{env_url}}/recordings/{{recording_id}}"
    }
  ]
}

Architectural Reasoning: Native Adaptive Cards provide a consistent UI across desktop and mobile Teams clients. They allow managers to drill down into the interaction without leaving the chat application. This reduces the friction between notification and action. The Action.OpenUrl blocks must use the correct environment URL ({{env_url}}) to ensure the links work for users in different Genesys Cloud environments (e.g., usw2.pure.cloud).

The Trap: Dynamic URL generation failures. If the interaction_id is null (due to the trap mentioned in Step 1), the URL becomes invalid, and the button breaks. Always wrap the URL generation in a conditional check. If interaction_id is empty, omit the action button or replace it with a generic “View Survey Responses” link.

Validation, Edge Cases & Troubleshooting

Edge Case 1: The “Silent” Detractor (Survey Abandonment)

The Failure Condition: Managers complain that they are not receiving alerts for customers who clearly had a bad experience but did not complete the survey.
The Root Cause: The flow is triggered only on Survey Response. Abandoned surveys do not trigger this event.
The Solution: Implement a secondary flow triggered by Interaction End. Use a Data Store to track interactions where the agent marked the call as “escalated” or “negative sentiment” (via Speech Analytics). If an interaction ends with a negative sentiment tag and no survey response is received within 24 hours, trigger a “Missed Opportunity” alert to the manager. This closes the loop for non-survey interactions.

Edge Case 2: Duplicate Alerts for Multi-Channel Interactions

The Failure Condition: A customer contacts via chat, then calls back, and receives two surveys. Both are detractors. The manager receives two identical alerts for the same underlying issue.
The Root Cause: The system treats each interaction as independent.
The Solution: Implement a deduplication mechanism using the customer_id or customer_email. In the Alerting Flow, add a Data Store Lookup for customer_id with a key of last_alert_timestamp. If the current time minus last_alert_timestamp is less than 24 hours, skip the alert. Update the timestamp after sending the alert. This prevents alert fatigue while still capturing the severity of repeated issues.

Edge Case 3: OAuth Token Expiry in Custom Webhooks

The Failure Condition: Alerts stop working randomly after 60-90 days.
The Root Cause: If you are using a custom API Request to a third-party service (e.g., a custom Slack bot endpoint) that requires OAuth, the access token expires. Architect does not automatically refresh tokens for custom API requests.
The Solution: Avoid custom OAuth endpoints for simple alerts. Use the native Send Teams Message or Send Slack Message blocks, which manage token lifecycle automatically. If you must use a custom webhook, implement a middleware service (e.g., AWS Lambda, Azure Function) that handles token refresh and caching, exposing a simple unauthenticated endpoint to Genesys Cloud.

Official References