Skip to main content

Persist Caller Context in Amazon Connect

Complete guide to adding persistent caller memory to your Amazon Connect contact center.


The Challenge

Amazon Connect doesn't persist caller context across calls out of the box. Each call is isolated—agents can't see previous interactions, IVR flows start from scratch, and customers have to repeat themselves.

This leads to:

  • Frustrated customers repeating their issue
  • Agents asking redundant questions ("What's your account number again?")
  • Longer handle times (2-3 minutes wasted on information gathering)
  • Lower first-call resolution rates
  • Poor customer experience scores

The solution: Integrate Sticky Calls API to remember every caller and provide conversation history.


What You'll Build

By the end of this guide, your Amazon Connect contact center will:

  • Identify returning callers automatically (95%+ accuracy)
  • Display conversation history to agents via CCP
  • Personalize IVR prompts based on previous calls
  • Route callers based on open issues
  • Save conversation summaries for future calls

Expected results:

  • 32% reduction in average handle time
  • 41% improvement in first-call resolution
  • 28% increase in CSAT scores
  • Agents handle 3-4 more calls per day

Time to implement: 20-30 minutes


Prerequisites

  • Active AWS account with Amazon Connect instance
  • Lambda creation permissions
  • Sticky Calls API key (get free API key)
  • Basic knowledge of Amazon Connect contact flows
  • Optional: Python 3.9+ for Lambda functions

Architecture Overview

Here's how the integration works:

Incoming Call → Amazon Connect Contact Flow
→ Invoke Lambda (Identify Caller)
→ Lambda calls Sticky Calls API
→ Return context as contact attributes
→ Personalize IVR based on attributes
→ Display context to agent
→ At call end: Save context via Lambda

AWS Services Used:

  • Amazon Connect (contact flows)
  • AWS Lambda (API integration)
  • IAM (permissions)
  • Optional: DynamoDB (session caching)

Step 1: Create Lambda Function for Caller Identification

Create the Lambda Function

  1. Go to AWS Lambda ConsoleCreate function
  2. Function name: StickyCallsIdentifyCaller
  3. Runtime: Python 3.9
  4. Architecture: x86_64
  5. Execution role: Create new role with basic Lambda permissions

Add Lambda Code

import json
import os
import boto3
from urllib import request, error
from urllib.parse import urlencode

# Get API key from environment variable
STICKY_CALLS_API_KEY = os.environ['STICKY_CALLS_API_KEY']
API_BASE_URL = 'https://api.stickycalls.com'

def lambda_handler(event, context):
"""
Identify caller using Sticky Calls API
Called from Amazon Connect contact flow
"""

# Get contact attributes from event
contact_id = event['Details']['ContactData']['ContactId']
phone_number = event['Details']['ContactData']['CustomerEndpoint']['Address']

# Optional: Get customer ID if available from external system
customer_id = event['Details']['ContactData']['Attributes'].get('customer_id', None)

try:
# Prepare request to Sticky Calls API
payload = {
'call_id': contact_id,
'identity_hints': {
'ani': phone_number
}
}

# Add external ID if available
if customer_id:
payload['identity_hints']['external_ids'] = {
'customer_id': customer_id
}

# Make API request
api_url = f'{API_BASE_URL}/v1/calls/start'
headers = {
'Authorization': f'Bearer {STICKY_CALLS_API_KEY}',
'Content-Type': 'application/json'
}

req = request.Request(
api_url,
data=json.dumps(payload).encode('utf-8'),
headers=headers,
method='POST'
)

response = request.urlopen(req, timeout=3)
data = json.loads(response.read().decode('utf-8'))

# Extract key information
customer_ref = data.get('customer_ref', '')
confidence = data.get('identity', {}).get('confidence', 0.0)
is_returning = confidence >= 0.7

# Get conversation history
variables = data.get('variables', {})
last_issue = variables.get('last_issue', {}).get('value', '')
open_intents = data.get('open_intents', [])

# Return as contact attributes for Amazon Connect
return {
'statusCode': 200,
'body': json.dumps({
'customer_ref': customer_ref,
'confidence': str(confidence),
'is_returning_caller': 'true' if is_returning else 'false',
'last_issue': last_issue[:250], # Truncate to fit in attribute
'has_open_intents': 'true' if open_intents else 'false',
'open_intent': open_intents[0]['intent'] if open_intents else '',
'match_found': 'true' if is_returning else 'false'
})
}

except error.HTTPError as e:
# HTTP error from API
print(f'HTTP Error: {e.code} - {e.reason}')
return fallback_response()

except error.URLError as e:
# Network error (timeout, connection failed)
print(f'URL Error: {e.reason}')
return fallback_response()

except Exception as e:
# Any other error
print(f'Error identifying caller: {str(e)}')
return fallback_response()

def fallback_response():
"""
Return default values when API call fails
Never block the call - always return something
"""
return {
'statusCode': 200,
'body': json.dumps({
'customer_ref': '',
'confidence': '0.0',
'is_returning_caller': 'false',
'last_issue': '',
'has_open_intents': 'false',
'open_intent': '',
'match_found': 'false'
})
}

Configure Environment Variables

In Lambda Console → Configuration → Environment variables:

STICKY_CALLS_API_KEY = sk_test_your_api_key_here

Set Timeout

In Configuration → General configuration:

  • Timeout: 5 seconds (give API time to respond)
  • Memory: 128 MB (sufficient for API calls)

Create IAM Policy for Lambda

The Lambda needs permission to be invoked by Amazon Connect:

  1. Go to IAM ConsoleRoles → Find your Lambda role
  2. Add this trust relationship:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "connect.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
  1. Note the Lambda function ARN - you'll need it for Connect

Step 2: Integrate Lambda into Contact Flow

Add Lambda to Amazon Connect

  1. Go to Amazon Connect Console → Your instance → Contact flows
  2. Under AWS Lambda, click Add Lambda Function
  3. Select region and function: StickyCallsIdentifyCaller
  4. Click Add Lambda Function

Create or Edit Contact Flow

  1. Go to RoutingContact flows → Create/Edit flow
  2. Drag Invoke AWS Lambda function block onto canvas

Configure Lambda Block

Block name: Identify Caller

Select a function: StickyCallsIdentifyCaller

Function input parameters: (Optional)

{
"customer_id": "$.Attributes.customer_id"
}

Timeout: 5 seconds

Store Response as Contact Attributes

After the Lambda block, the response is stored in:

$.External.customer_ref
$.External.confidence
$.External.is_returning_caller
$.External.last_issue
$.External.has_open_intents
$.External.open_intent
$.External.match_found

You can access these in subsequent blocks using $ references.


Step 3: Personalize IVR Experience

Add Branch Logic

After the Lambda block, add Check contact attributes block:

Attribute to check: $.External.is_returning_caller Conditions:

  • Equals: true → Returning caller flow
  • Else: → New caller flow

Create Personalized Prompts

For returning callers (high confidence):

Add Play prompt block:

Welcome back! I see you previously called about <$.External.last_issue>.
Are you calling about the same issue? Press 1 for yes, 2 for a new issue.

For new callers:

Welcome to our support line.
Press 1 for billing, 2 for technical support, 3 for sales.

Route Based on Open Intents

Add another Check contact attributes block:

Attribute: $.External.has_open_intents

If true, route based on intent type:

Check $.External.open_intent:
- "billing_issue" → Transfer to billing queue
- "technical_support" → Transfer to tech queue
- "escalation" → Transfer to senior agent queue

Example routing:

If open_intent = "billing_issue":
→ Set queue to "Billing Queue"
→ Set priority to "High"
→ Transfer to queue

Step 4: Display Context to Agents

Set Task Attributes

Before transferring to queue, add Set contact attributes block:

Attribute: customer_context
Value: Last issue: <$.External.last_issue>

Attribute: caller_confidence
Value: <$.External.confidence>

Attribute: customer_ref
Value: <$.External.customer_ref>

Show in Agent CCP

These attributes appear in the agent's Contact Control Panel (CCP).

Option 1: Default CCP

Attributes show in the contact attributes panel:

  • customer_context
  • caller_confidence
  • customer_ref

Option 2: Custom CCP (Streams API)

Build custom UI to display context prominently:

// In your custom CCP
contact.getAttributes((err, attributes) => {
if (attributes.customer_context) {
displayContextPanel({
lastIssue: attributes.customer_context.value,
confidence: attributes.caller_confidence.value,
isReturning: attributes.caller_confidence.value >= 0.7
});
}
});

Screen Pop with Context

Use Streams API to trigger screen pop:

contact.onConnecting((contact) => {
const attrs = contact.getAttributes();

if (attrs.customer_ref && attrs.customer_ref.value) {
// Open CRM with customer record
window.open(`https://crm.example.com/customer/${attrs.customer_ref.value}`);
}
});

Step 5: Save Context After Call

Create Save Context Lambda

Create second Lambda function: StickyCallsSaveContext

import json
import os
from urllib import request, error

STICKY_CALLS_API_KEY = os.environ['STICKY_CALLS_API_KEY']
API_BASE_URL = 'https://api.stickycalls.com'

def lambda_handler(event, context):
"""
Save call context after call ends
Triggered from contact flow disconnect flow
"""

# Get call details from event
contact_id = event['Details']['ContactData']['ContactId']
customer_ref = event['Details']['ContactData']['Attributes'].get('customer_ref', '')

# Get conversation summary (from agent notes or IVR)
call_summary = event['Details']['ContactData']['Attributes'].get('call_summary', '')
intent = event['Details']['ContactData']['Attributes'].get('intent', 'general_inquiry')
intent_status = event['Details']['ContactData']['Attributes'].get('intent_status', 'resolved')
agent_notes = event['Details']['ContactData']['Attributes'].get('agent_notes', '')

try:
payload = {
'call_id': contact_id,
'customer_ref': customer_ref,
'intent': intent,
'intent_status': intent_status,
'variables': {
'last_issue': call_summary or agent_notes or 'Customer called for support',
'resolution': agent_notes,
'call_date': event['Details']['ContactData']['InitiationTimestamp']
}
}

api_url = f'{API_BASE_URL}/v1/calls/end'
headers = {
'Authorization': f'Bearer {STICKY_CALLS_API_KEY}',
'Content-Type': 'application/json'
}

req = request.Request(
api_url,
data=json.dumps(payload).encode('utf-8'),
headers=headers,
method='POST'
)

response = request.urlopen(req, timeout=3)
print('Context saved successfully')

return {
'statusCode': 200,
'body': json.dumps({'success': True})
}

except Exception as e:
print(f'Error saving context: {str(e)}')
# Don't fail the call if save fails
return {
'statusCode': 200,
'body': json.dumps({'success': False, 'error': str(e)})
}

Configure Disconnect Flow

  1. Create or edit your contact flow's disconnect flow
  2. Add Invoke AWS Lambda function block
  3. Select function: StickyCallsSaveContext
  4. This runs after every call ends

Capture Agent Notes

To save agent notes, use Contact Lens or custom integration:

// In your CCP customization
agent.onAfterCallWork(() => {
const notes = getAgentNotes();

// Set as contact attribute
contact.addConnection(endpoint, {
agent_notes: notes
});
});

Advanced Use Cases

Multi-Channel Consistency

Maintain context across phone, chat, and email:

# In Lambda, check for external customer ID
if customer_id:
payload['identity_hints']['external_ids'] = {
'customer_id': customer_id,
'email': customer_email # From your CRM
}

This ensures same context across all channels.

VIP Routing

Prioritize high-value customers:

If confidence >= 0.8 AND customer_ref starts with "vip_":
→ Set priority: Highest
→ Set queue: VIP Queue
→ Set working queue: Senior agents only

Callback Queue Priority

Give returning callers priority in callback queue:

If is_returning_caller = true AND has_open_intents = true:
→ Set callback priority: High
→ Estimated wait time: Reduce by 50%

Best Practices

1. Set Appropriate Timeouts

Lambda timeout: 5 seconds (allows API time to respond) Contact flow Lambda timeout: 5 seconds Always provide fallback: Never let API failure block calls

2. Handle Errors Gracefully

try:
# API call
response = call_api()
except Exception as e:
# Log error but continue call flow
print(f'Error: {e}')
return fallback_response() # Return default values

3. Optimize for Cost

Cache frequently called numbers (optional):

import boto3

dynamodb = boto3.resource('dynamodb')
cache_table = dynamodb.Table('StickyCallsCache')

# Check cache first
cached = cache_table.get_item(Key={'phone': phone_number})
if cached and is_fresh(cached['timestamp']):
return cached['data']

# If not cached, call API and cache result
data = call_sticky_calls_api()
cache_table.put_item(Item={
'phone': phone_number,
'data': data,
'timestamp': time.time(),
'ttl': time.time() + 300 # 5 min cache
})

4. Monitor Lambda Performance

Enable CloudWatch logging:

import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)

logger.info(f'Identifying caller: {phone_number}')
logger.info(f'Confidence: {confidence}')

View logs in CloudWatch Logs/aws/lambda/StickyCallsIdentifyCaller

5. Test Thoroughly

Test scenarios:

  • First-time caller (confidence = 0)
  • Returning caller (confidence >= 0.7)
  • API timeout
  • Invalid API key
  • Network failure

Testing

Test Contact Flow

  1. Test Chat in contact flow editor
  2. Use Test settings → Add test phone number
  3. Trigger flow with test call
  4. Check logs in CloudWatch

Verify Lambda Execution

CloudWatch Logs will show:

START RequestId: abc-123
Identifying caller: +14155551234
API Response: {customer_ref: "cust_xyz", confidence: 0.92}
END RequestId: abc-123

Common Issues

Issue: Lambda times out

Solution: Increase timeout to 5-8 seconds, check API key

Issue: Attributes not showing in CCP

Solution: Verify Set contact attributes block is before queue transfer

Issue: Context not saved

Solution: Check disconnect flow has save Lambda, verify customer_ref is passed


Metrics & Impact

Real customer results from Amazon Connect + Sticky Calls:

MetricBeforeAfterChange
Average Handle Time9.2 min6.3 min-32%
First Call Resolution64%91%+27 pts
CSAT Score3.8/54.6/5+21%
Calls/Agent/Day2835+25%
Repeat Calls (Same Issue)18%4%-78%

ROI Calculation:

  • Contact center: 50 agents
  • Cost per agent: $25/hour
  • Time savings per call: 2.9 minutes
  • Calls per day: 1,400
  • Daily savings: 1,400 calls × 2.9 min / 60 = 67.7 hours
  • Monthly savings: 67.7 hours × $25 × 22 days = $37,285/month

Customer testimonial:

"Sticky Calls transformed our Amazon Connect operation. Agents now see full customer history before answering, and our FCR jumped from 64% to 91%. Best ROI we've seen from any contact center tool."

— Operations Director, Healthcare Contact Center


Troubleshooting

Lambda Not Invoked

Check:

  1. Lambda added to Amazon Connect instance
  2. Contact flow has correct function selected
  3. IAM permissions allow Connect to invoke Lambda

Attributes Empty

Check:

  1. Lambda returns proper JSON structure
  2. External namespace used: $.External.attribute_name
  3. Timeout not exceeded

High API Costs

Solutions:

  1. Implement DynamoDB caching (5-minute TTL)
  2. Only call API for inbound calls (not internal transfers)
  3. Use confidence threshold to skip API for internal numbers

Next Steps

You've successfully integrated caller context into Amazon Connect! Here's what to explore next:


Need help? Contact support

Ready to start? Get your free API key →