Skip to main content

Amazon Web Services

Building an Amazon Connect Holiday Calendar in 4 Easy Steps

Between calendars, the check queue status node as well as the capacity settings for each queue, Amazon Connect administrators have several options to handle a busy work day. However, there are some scenarios where the routing behavior needs to be customized beyond these out of the box options. One example would be setting up a holiday hours calendar well in advance.

By default, the Amazon Connect hours of operation are perfect for setting up regular week-long calendars that handle multiple shifts and routine breaks. However, to handle infrequent changes such as time off for New Year’s day it makes sense to use Lambda along with a DynamoDB calendar.  We’ve previously covered the steps necessary to set up an emergency call flow, and the same concepts can be used to provide the caller with a season appropriate greeting or make sure they don’t get stuck in a closed queue because the regular calendar was not updated appropriately.

DynamoDB set-up

The first step you will need to take is create a table to store all our holiday dates. Depending on the exact use case there are a few ways you could set things up. If your call center only takes whole days off it might make sense to just save the month and day then query the table with today’s date. If you’re only interested in updating the greeting with a custom message having a simpler table that only stores a date and corresponding text to read back would be easier.

For our use case, we want to have a start timestamp for the holiday as well as an end timestamp. This will allow us to call Lambda with a timestamp for each call and check if it’s between any of our predetermined holiday hours then branch based on the results. We will also want to play a different message depending on the holiday so along with the two dates we will save a holiday name as a string. Since the dates should not overlap you can simply use the dateStart as your primary key.

After the table is created add a few items using either the tree or JSON text entry options. Make sure to append a dateEnd and a holiday name alongside your primary key. Also keep in mind that dateStart and dateEnd should be timestamps (for example midnight December 25th GMT will be represented as 1514160000000). Using a timestamp conversion tool should make things easier and at the end of the blog we will also go over another way to update the table. After entering a few values your table should look similar to the image below (note that for testing purposes we entered a test holiday you should make sure to remove before go-live).

 

Lambda set-up

With the DynamoDB table in place it’s time to look at the code we will use to check where our date falls in the calendar. Create a new Lambda function and select the author from scratch option. You can name it something that makes sense for you, select the Node.js run-time and make sure you select a role that can access Lambda.

In case you don’t already have an IAM role set up for database access you can easily create one using the visual IAM guide. Keep in mind it’s always best to limit access on a need to have basis. It might make sense to create a restricted read only role and even if you allow full access to DynamoDB it’s best to limit access to just one table via the resource section. Here is an example of how your role for this project might look.

{

    "Version": "2012-10-17",

    "Statement": [

        {

            "Action": [

                "dynamodb:*",

            ],

            "Effect": "Allow",

            "Resource": "arn:YOUR DYNAMODB TABLE ARN"

        },

     

    ]

}

Once you have the appropriate role in place you can create the function and enter the following code in the in-line editor.

const AWS =require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});

exports.handler = (event, context, callback) => {
    
     const scanningParams ={
       TableName:'connect-holiday-schedule',
       Limit:100,
       FilterExpression: ":dateNow between dateStart and dateEnd",
       ExpressionAttributeValues: {
         ":dateNow": Date.now()
         
    }
   };
  
  docClient.scan(scanningParams, function(err,data){
      if(err){
          callback(err,null);
      }else{
          if(data.Items[0] == null){
              const noHolidayResponse = JSON.stringify({reason: 'noHoliday'});
              callback(null,JSON.parse(noHolidayResponse ));
          }
          else{
          callback(null,data.Items[0]);
          }
      }
  });
};

This Lambda function will scan the table and return results where the current date is between the dateStart and dateEnd values. Scanning a table is not necessarily the most resource efficient solution, but for our small holiday schedule it will work well enough. If you expect a much larger table you should consider running a query instead.

Something to note is that we are returning a hard-coded “noHoliday” message if we don’t find any results and we are only returning one item back (data.Items[0]) in case of a match. It’s possible that our function will find more than one matching result, but at this time passing back anything beyond a key:value list to Amazon Connect will result in an error.

Something like this will work well.

{

  "dateEnd": 1514277900000,

  "dateStart": 1513617545572,

  "reason": "Test Holiday"

}

 

Amazon Web Services - Avoid Contact Center Outages: Plan Your Upgrade to Amazon Connect
Avoid Contact Center Outages: Plan Your Upgrade to Amazon Connect

Learn the six most common pitfalls when upgrading your contact center, and how Amazon Connect can help you avoid them.

Get the Guide

But if your results look like this Amazon Connect won’t be able to parse it and will throw “Results”: “The Lambda Function Returned An Error.”

[

{    "dateEnd": 1514275200000,    "dateStart": 1514102400000,    "reason": "Christmas"  },
{    "dateEnd": 1514880000000,    "dateStart": 1514793600000,    "reason": "New Year Day"  },
{    "dateEnd": 1514277900000,    "dateStart": 1513617545572,    "reason": "Test Holiday"  }

]

Also note that we are using Date.now(), which will return a timestamp. If you don’t need hours you can just grab the day and month or filter on another property.

Finally, if you are not familiar with node.js you can easily use another programing language to achieve the same result. The getting started with DynamoDB documentation has examples for several languages you can use to query a table.

With the function in place you can run a test with a blank JSON since we are not passing any values into the function. If you are building Lambda functions over Christmas or you set up a test holiday appropriately you should see a reason for celebration being returned. Otherwise you should see the “noHoliday” result.

Before moving to the Amazon Connect contact flow, make sure you add the appropriate permissions via the AWS CLI. . You can find more details on how to do this in the official integration documentation.

Amazon Connect set-up

Since we want to make sure all callers are aware the office is closed for New Year’s Day, the contact flow below should be the first entry point into the call center. To keep things easy to manage it’s recommended to set it up in a separate flow that will then transfer to a menu or a regular calendar check after doing the Lambda call.

The very first node should be an Invoke AWS Lambda Function calling the function we created in the previous step. All you will need is the ARN found in the upper left corner of the function page. You can leave the timeout in place since the query should be very fast and we won’t pass in any parameters.

Lambda will automatically generate the date/time when it was called and check if it’s present in the table, returning either a holiday reason or “noHoliday”. In order to verify what comes back you will make use of a Check contact attributes node.

Make sure to enter all holiday options and route them down the appropriate path. For testing purposes you can simply set up a prompt node that will read back the external item found in “reason” ($External.reason) . Keep in mind the noHoliday option will be your default behavior and can transfer into the regular call center flow.

At this point you are essentially done with the Connect integration, however reading your holiday table is not the easiest task since everything is entered as a timestamp. Also updating a new Holiday will take some work including a timestamp conversion tool.

Luckily AWS makes it easy to set up a simple static website within S3 that can read entries from a table and even allow supervisors to upload a new holiday.

Setting up an admin website

We will use two slightly different Lambda functions to display and update data on our web page. You can create a Holiday Read function that will grab all data available and look similar to this.

const AWS =require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});

exports.handler = (event, context, callback) => {
    
     var scanningParams ={
       TableName:'connect-holiday-schedule',
       Limit:100,

   };
  
  docClient.scan(scanningParams, function(err,data){
      if(err){
          callback(err,null);
        }
      else{
          callback(null,data);
          }
      });
}

It is essentially the same function we used earlier in the Amazon Connect contact flow, but this time we are not filtering out one entry instead returning everything we find in the DynamoDB table.

The table update code will be a bit more involved since it needs to accept 3 data points entered by a supervisor and make sure it can accept a connection from an external website. Please note that the code below will accept requests from any website domain. This is not ideal and in practice you should limit requests to a specific domain by changing the Access-Control-Allow-Origin headers.

const AWS =require('aws-sdk');
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'});

exports.handler = (event, context, callback) => {
    console.log(event.body);

    var obj = JSON.parse(event.body);

    console.log(obj.dateStart);
    
   const params ={
       Item:{
           dateStart: obj.dateStart,
           dateEnd: obj.dateEnd,
           reason: obj.reason 
       },
       TableName:'connect-holiday-schedule'
   }
  
  console.log(params);
  
  
  docClient.put(params, function(err,data){
      if(err){
          callback(err,null);
      }else{
          const response = {
      statusCode: 200,
      headers: {
        "Access-Control-Allow-Origin" : "*", // Required for CORS support to work
        "Access-Control-Allow-Credentials" : true 
      },
      body: JSON.stringify({ "message": "Success!" })
    };

    callback(null, response);
      }
  });
    
 
};

With both functions created you can take a look at configuring the API gateway. This service will allow us to make a call to our Lambda functions from an outside source. If you are not familiar with configuring API gateway reading through the getting started documentation should get you started.  You will need to create a new API, a resource and add a GET as well as a POST method.

The GET method should be pretty straightforward and once you select integration type as Lambda Function you will be able to select your Lambda function for reading table entries.

The post method can be set up as a proxy integration as shown below.

Finally make sure to enable CORS for the resource you created. You can do this by selecting the resource and navigating to Actions. For testing purposes you can enable access from any origin by using Access-Control-Allow-Origin:’*’. As noted above this will allow anyone to access your table and should not be kept in production.

After enabling CORS you can deploy the API and build the actual website your supervisors can check. There are several ways you can handle calling the API gateway we just created and display the values received back, depending on your favorite front-end approach. Here is a github repo for a pretty bare-bones example of how you can use jQuery to display all holidays and allow new entries.

Once you have created your web page you can simply upload it in an S3 bucket configured to host a static website. Now your call center supervisors can see all holidays entered in the system and add new ones if need be.

Hopefully this post gave you some ideas on how you can better customize your own call center, but if you have any questions around configuration, best practices or how to build a unique integration please reach out to Craig Reishus.

Thoughts on “Building an Amazon Connect Holiday Calendar in 4 Easy Steps”

  1. Great guide Alex,

    Amazon actually use this guide as an example in their Connect workshops, something which I’ve recently taken part in myself.

    I thought it would be useful if this whole process could be automated so instead of setting up an admin site like you’ve done, I’ve written a lambda function that calls the gov.uk bank holiday JSON file, formats it and enters it into the DynamoDB table. All that you would therefore need to do would be to schedule the function once a year and sit back!

    Anyone that wants to use it, can grab it here: https://github.com/spadgezilla/Amazon-Connect-Bank-Holiday-Automation

    Hopefully this is useful to someone out there and I’ve tried to comment the code to the point where someone with basic understanding of javascript could swap out things like the source file and variables to call. I’m by no stretch of the imagination a coder so apologies if it’s a bit messy.

  2. Nice write up. The problem with this is it doesn’t take timezones (or even DST) into account. A caller might experience “holiday logic” even when it’s not a holiday.

  3. Alex Moisi Post author

    While there is no logic for dynamically updating holidays by using a Unix timestamp we can account for some of the scenarios you’re describing. You’d just have to be careful exactly what timestamp you’re entering.

  4. Alex Moisi Post author

    The main reason is flexibility and making it more user friendly to update holidays long term, but you are correct this can be all hard-coded in the Lambda.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Alex Moisi

Alex Moisi is a Senior Technical Consultant at Perficient focusing on call center solutions including Amazon Connect.

More from this Author

Follow Us
TwitterLinkedinFacebookYoutubeInstagram