AWS Contact Center

Best practices for building a Customer Effort Score (CES) system with Amazon Connect Customer

Your Amazon Connect Customer contact center measures customer satisfaction (CSAT), but CSAT doesn’t predict whether customers will stay. Research suggests high-effort service interactions are the strongest driver of customer disloyalty. Customer Effort Score (CES) measures how easy it was to get an issue resolved — the strongest predictor of loyalty and churn.

Most organizations lack a systematic way to measure effort. Amazon Connect Customer provides the data and capabilities to measure CES automatically — both explicitly and implicitly — across contacts.

This post shows:

  • How to capture explicit effort scores using post-contact surveys
  • How to detect implicit effort signals from Contact Trace Records using Amazon Athena
  • How to operationalize CES with effort-aware routing, alerting, and proactive outreach

Prerequisites

To implement the implicit effort scoring described in this post, you need:

  • An Amazon Connect Customer instance with Contact Trace Records (CTRs) enabled
  • Amazon Data Firehose configured to stream CTRs to an Amazon S3 bucket (configure under Data storage > Contact Trace Records in the Amazon Connect Customer console)
  • An Amazon Athena table created over your CTR data in S3 using the JsonSerDe with case.insensitive = true
  • (Optional) Amazon Connect Customer conversational analytics enabled for sentiment-based alerting

For the explicit survey, you only need an Amazon Connect Customer disconnect flow with a Get Customer Input block.

Best practice 1: Capture explicit effort with post-contact surveys

The most direct way to measure effort is to ask. Configure a single-question survey in your disconnect flow: “On a scale of 1 to 7, how easy was it to resolve your issue today?”

Amazon Connect Customer supports three delivery mechanisms:

  • Disconnect flow interactive voice response (IVR) — customer rates using their keypad before hanging up
  • Amazon Connect Customer Tasks — trigger an outbound SMS or email survey post-interaction
  • Post-chat survey — present the question in the chat widget after the conversation ends

To configure: edit your disconnect flow, add a Get Customer Input block with dual-tone multi-frequency (DTMF) input (1-7), and store the response as a contact attribute (ces_score). The response persists in the contact record’s Attributes field as a queryable key-value pair.

The limitation is survey response rates are low, typically capturing only a fraction of total contacts. This gives you ground truth for calibration but not full coverage. For that, you need implicit measurement.

Best practice 2: Detect implicit effort from interaction signals

Every CTR contains metadata that correlates with customer effort. By querying CTR data in Amazon Athena, you can score effort across the vast majority of contacts without asking. For the complete CTR schema, see the Contact Trace Records data model.

High-effort signals and weighted scoring model

The following scoring model assigns weights to CTR signals based on their correlation with customer effort:

Signal Weight Rationale
Transfer detected +4 Customer re-explained their issue
Repeat contact within 7 days (voice) +4 per occurrence (cap +12) Compounding resolution failure
NumberOfHolds > 1 +2 Multiple holds signal confusion
CustomerHoldDuration > 120s +2 Extended waiting
AgentConnectionAttempts > 2 +1 Difficulty reaching an agent
First-contact resolution -3 Issue resolved cleanly

These weights are starting points. Calibrate against your explicit survey data over 30 days before using implicit scores to make decisions.

Note: NextContactId is used as a transfer proxy. In environments using queued callbacks, consider adding AND c.initiationmethod != 'CALLBACK' to the transfer detection condition to avoid false positives.

Channel considerations:

  • Repeat contact detection uses CustomerEndpoint.Address (phone number), reliable for voice only. Chat requires Amazon Connect Customer Profiles for consistent CustomerId across sessions.
  • Task contacts are excluded as they represent internal work items, not customer-initiated interactions.
  • Amazon Connect Customer conversational analytics sentiment data is stored separately from CTRs (in the Analysis/ prefix of your S3 bucket). Use conversational analytics rules to configure real-time sentiment alerting rather than incorporating it into the Athena query.

Effort scoring query

The following Athena query scores every contact (excluding tasks) and detects repeat voice contacts within a 7-day window:

WITH deduped AS (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY contactid ORDER BY initiationtimestamp DESC) AS rn
  FROM contact_records
  WHERE contactid IS NOT NULL AND contactid != ''
    AND channel != 'TASK'
    AND disconnecttimestamp IS NOT NULL
),
clean AS (
  SELECT * FROM deduped WHERE rn = 1
),
repeat_counts AS (
  SELECT a.contactid, COUNT(DISTINCT b.contactid) AS previous_contact_count
  FROM clean a
  INNER JOIN clean b
    ON a.channel = 'VOICE' AND b.channel = 'VOICE'
    AND a.customerendpoint.address = b.customerendpoint.address
    AND a.contactid != b.contactid
    AND from_iso8601_timestamp(b.disconnecttimestamp)
        BETWEEN date_add('day', -7, from_iso8601_timestamp(a.initiationtimestamp))
        AND from_iso8601_timestamp(a.initiationtimestamp)
  GROUP BY a.contactid
)
SELECT c.contactid, c.channel, c.queue.name AS queue_name,
  COALESCE(r.previous_contact_count, 0) AS repeat_contacts_7d,
  (CASE WHEN c.agent.numberofholds > 1 THEN 2 ELSE 0 END)
  + (CASE WHEN c.agent.customerholdduration > 120 THEN 2 ELSE 0 END)
  + (CASE WHEN c.agentconnectionattempts > 2 THEN 1 ELSE 0 END)
  + (CASE WHEN c.nextcontactid IS NOT NULL THEN 4 ELSE 0 END)
  + (LEAST(COALESCE(r.previous_contact_count, 0), 3) * 4)
  - (CASE WHEN c.nextcontactid IS NULL
       AND c.agent.numberofholds = 0 THEN 3 ELSE 0 END)
  AS implicit_effort_score
FROM clean c
LEFT JOIN repeat_counts r ON c.contactid = r.contactid
WHERE from_iso8601_timestamp(c.initiationtimestamp) >= TIMESTAMP 'YYYY-MM-DD 00:00:00'; -- Replace with your desired start date

The query deduplicates CTRs (Firehose may deliver duplicates), filters abandoned contacts with null timestamps, and caps repeat scoring at 3 occurrences to prevent outlier inflation.

Best practice 3: Operationalize effort scores to drive action

Trend monitoring: Aggregate scores by queue and time period. A rising effort score before CSAT drops is your early warning for process breakdown.

Sentiment-based alerting: Configure Amazon Connect Customer analytics rules to flag contacts with negative sentiment in the first 30 seconds. Alert supervisors for real-time intervention on returning high-effort customers.

Effort-aware routing: Use an AWS Lambda function to query a customer’s recent effort history from Amazon DynamoDB, set a contact attribute (effort_level = high), and route via Check Contact Attributes to a priority queue.

Implementation:

  1. Store implicit effort scores in an Amazon DynamoDB table keyed by customer phone number (CustomerEndpoint.Address), updated after each contact by a post-call Lambda trigger
  2. In your inbound contact flow, add an Invoke AWS Lambda Function block early in the flow that queries DynamoDB for the caller’s effort history from the past 30 days
  3. The Lambda function returns an effort_level attribute (high, medium, low) based on the customer’s average implicit score
  4. Add a Check Contact Attributes block that branches on the effort_level attribute
  5. Route “high” effort customers to a priority queue with tenured agents and reduced hold targets

Lambda function — query customer effort level

import boto3
from datetime import datetime, timedelta
from boto3.dynamodb.conditions import Key

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('customer_effort_scores')

def lambda_handler(event, context):
    phone = event['Details']['ContactData']['CustomerEndpoint']['Address']
    cutoff = (datetime.utcnow() - timedelta(days=30)).isoformat()
    
    response = table.query(
        KeyConditionExpression=Key('customer_phone').eq(phone) & Key('contact_date').gt(cutoff)
    )
    
    scores = [item['implicit_effort_score'] for item in response['Items']]
    avg_score = sum(scores) / len(scores) if scores else 0
    
    if avg_score >= 8:
        effort_level = 'high'
    elif avg_score >= 4:
        effort_level = 'medium'
    else:
        effort_level = 'low'
    
    return {'effort_level': effort_level, 'avg_effort_score': str(round(avg_score, 1))}

Proactive outreach: When a contact’s effort score exceeds your threshold, call the StartTaskContact API to generate a follow-up task within 24 hours. A targeted follow-up contact can help address unresolved concerns and improve the customer experience.

Quarterly process review: Identify the three journeys with the highest average effort scores each quarter. These represent your highest-priority improvement opportunities.

Cleanup

The Amazon Athena table and S3 data can be retained for ongoing CES analysis at minimal cost. If you no longer need the effort scoring system, delete the Athena table and configure an S3 lifecycle policy on your CTR bucket to manage storage costs. No other resources require cleanup unless you deployed the optional Lambda function and DynamoDB table for effort-aware routing.

Conclusion

Customer Effort Score fills the gap between satisfaction measurement and churn prediction. By combining explicit surveys with implicit CTR signals, you measure effort across interaction captured in CTRs, not just the ones where customers respond to surveys.

Start here: enable Amazon Connect Customer conversational analytics, add a one-question effort survey to your disconnect flow, and run the Athena query. Calibrate for 30 days, then operationalize with routing, alerting, and quarterly reviews.

CSAT tells you how customers feel. CES tells you what they’ll do next. Begin measuring effort today to anticipate customer churn before it happens.

To get started, visit the Amazon Connect Customer console. For more best practices, explore the Amazon Connect Customer Administrator Guide.


About the author

Headshot of Pamela Vasquez PascualPamela Vasquez Pascual is a Technical Account Manager for ISV customers at AWS. She specializes in contact center architecture, cost optimization, and operational excellence. She works with customers to scale their cloud infrastructure and build customer experience platforms on AWS. Outside of work, she enjoys home improvement projects and spending time with family.