Receiving voice input with Amazon Connect
Matt Lewis
Posted on February 5, 2024
I wrote a blog post last week that looked at two different options for making an outbound voice call to a customer to provide them with a piece of information such as a One Time Password (OTP).
One question I was asked following this, was what would happen if you needed the customer to provide you with some information over the phone (whether a passcode, or credit card number, or some other type of identification).
After discounting Amazon Pinpoint last week, let's take a look at how you can capture and validate data input by a customer with Amazon Connect.
This solution builds on the demo application from the last blog post which can be found here.
The final part of the contact flow when complete looks as follows:
Store Customer Input
The first block is a Store Customer Input
. I include an audio prompt that tells the customer to enter their 5 digit passcode followed by the hash key as a terminating key press. The digits entered via DTMF are saved in the Stored customer input system attribute. This block also provides additional capabilities, such as allowing you to encrypt the data.
Invoke AWS Lambda Function
The Store Customer Input
is connected to an Invoke AWS Lambda function
block. This is used to invoke a Lambda function and return key/value pairs that are set as contact attributes. The Function ARN
can only be set once we have created our Lambda function, but we can set the function input to include the value captured in the previous block in an attribute called enteredOTP
.
Create AWS Lambda Function
Next up is to create the actual AWS Lambda function that the block will invoke. To keep this simple, we create a new function in the AWS Lambda console using the Hello world function
blueprint as shown in the screen shot below:
If we were to try and invoke this function now from our contact flow, it would fail with an AccessDeniedException
. We would be able to see this in the CloudWatch Log Group for the Amazon Connect instance:
{
"Results": "Status Code: 403; Error Code: AccessDeniedException; RequestId: 404024fb-caac-449f-bec9-7d1fbde36a14",
"ContactId": "87a81f2f-b529-4b83-a0af-4a540dd9ac85",
"ContactFlowId": "arn:aws:connect:eu-west-2:424727766526:instance/83dfabfe-aced-428a-9595-faab3db13c20/contact-flow/a24c9bb7-062e-45c0-a138-962c4efde045",
"ContactFlowName": "Outbound Call OTP",
"ContactFlowModuleType": "InvokeExternalResource",
"Identifier": "Invoke OTP Function",
"Timestamp": "2024-01-29T21:26:12.039Z",
"Parameters": {
"FunctionArn": "arn:aws:lambda:eu-west-2:424727766526:function:logOTPFunction",
"ResponseValidation": "ResponseType=STRING_MAP",
"TimeLimit": "3000"
}
}
The AccessDeniedException
error message means the AWS Lambda function's resource-based policy doesn't grant Amazon Connect permission to invoke the function.
To fix this, we can go into the AWS Lambda console and click on add permissions:
And then add the following information:
The source ARN is the ARN of your Amazon Connect instance. This policy grants permission for your Amazon Connect instance to invoke this function.
We edit the code of the Lambda function in the console to the following:
export const handler = async (event, context) => {
console.log('Received event:', JSON.stringify(event, null, 2));
const originalOTP = event.Details.ContactData.Attributes.OTP;
const inputOTP = event.Details.Parameters.enteredOTP;
console.log(`Original OTP was ${originalOTP} and input OTP is ${inputOTP}`);
let responseMessage;
let isValid = false;
if (originalOTP === inputOTP) {
responseMessage = 'The number you entered is the same value as we sent you';
isValid = true;
} else {
responseMessage = 'The number you entered is not equal to the value we sent you';
isValid = false;
}
const resultMap = {
'responseMessage': responseMessage,
'isValid': isValid
}
return resultMap;
};
The first thing this code does is to extract the original OTP that is passed into the contact flow when it is invoked. You can find out more in the first blog post. This shows that when the StartOutboundVoiceContactCommand
API call is made, it is passed in an attribute with the key of OTP
const params = {
DestinationPhoneNumber: {numberToCall},
ContactFlowId: {contactFlowID},
InstanceId: {connectInstanceId},
QueueId: {QueueId},
Attributes: { // Attributes
"OTP": {OTP},
},
TrafficType: "GENERAL",
};
The value the customer entered on their keypad is retrieved from the parameters section with the key value as specified in the function parameter input section as shown previously.
Now we are in the Lambda function, we can make external API calls, or perform system lookups, or any other logic required. We just quickly compare the two passcode values to see if they are the same, and then return a message and a boolean value in a result map. This is essentially just a JSON structure.
Set Contact Attributes
The Invoke AWS Lambda function
block is connected to a Set Contact Attributes
block.
This block is used to copy the attributes returned in the JSON structure from the Lambda function into user-defined attributes.
You start by clicking in the block to add another attribute. The attribute value will be mapped to a key in the user defined namespace against the current contact. You set the value dynamically using the key name from the JSON structure returned and the External
namespace.
Play Prompt
Finally, we add a Play Prompt
block to the flow and set the text to spoken to the customer to the value of the response message attribute saved in the previous block: $.Attributes.ResponseMessage
Watch the following video to see this in action:
Posted on February 5, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.