Dynamic Contact Center Opening Hours Powered by Twilio Studio and Google Places

May 26, 2022
Written by
Reviewed by

Dynamic Contact Center Opening Hours with Google Places

The Problem

One of the most frequent asks when building out an IVR for a contact center is for the IVR to change when the contact center is closed. To deliver a great customer experience, you may want to direct the customer online, give them different options to self-serve, send them to voicemail or simply ask them to call back at a different time.

This kind of functionality can be built out relatively easily using Twilio Functions with Twilio Studio to lookup a spreadsheet or database and some kind of logic to decide if your business is currently open. However, this can become complex when managing public holidays, business downtime or other exceptions.

How Can This Be Easier?

In the online-first world that we live in, most businesses have a presence on Google, and some are even striving to be the most visible in their sector or region. As such, Google now provides your business with Google Business Profiles.

Screenshot of a Google Maps card for Twilio HQ showing office opening times as well as photos and reviews.

This is where you can create a profile and share with consumers data such as address, business information, pictures, reviews and importantly for this subject, opening hours.

Animated GIF of Jeff Lawson (Twilio CEO) asking "why isn't that an API?"

It is! Google provides this data via an API called Google Places API, so in this post you will use the power of this API along with the integrations that the Twilio Studio platform allows, to let your customers know if your business is open, or closed.

Prerequisites

Before starting, you will need a few things:

Solution Overview

This solution is quite a straight-forward one and looks like this:

Flow chart of the process described in prose beneath.
  1. A call comes in to the Twilio phone number
  2. The Twilio Studio Flow is triggered
  3. The Studio Flow calls a Serverless Function with the ID of the Google business
  4. The Serverless Function makes a request to Places API and returns open or closed to the Studio Flow
  5. The Studio Flow makes a decision on what action to take based on the result.

Serverless Function Build

The first thing you need to do is to create our serverless function that will act as the proxy between your studio Flow and the Google Cloud Platform, which you can do directly in the CLI. Open up your shell and use the command:

twilio serverless:init open-hours --empty --typescript

This command will create a blank Twilio Serverless project with the name from the first argument (in this case open-hours) with the configuration and directory structure that is required. You use the --typescript argument at the end to set up this project for development in TypeScript rather than JavaScript.

Screenshot of a terminal running the command described in prose. Output ends with "Success!"

Now you should install the Google Maps JavaScript SDK that you require to work with Google Cloud Platform. To do this, at the base of your serverless project run the following command:

npm i @googlemaps/google-maps-services-js

At this point, you can create your function by adding a file get-open-hours.protected.ts into the functions folder that was created during initialisation.

open-hours 
└───functions
│      │ get-open-hours.protected.ts

The .protected in this context means that only calls to this function from within the Twilio platform will be accepted (see Understanding Visibility of Functions and Assets for more info).

Then, inside of the newly created file you can add the following code

import {Client} from "@googlemaps/google-maps-services-js";
// Imports global types
import '@twilio-labs/serverless-runtime-types';

// Fetches specific types
import {
  ServerlessCallback,
  ServerlessFunctionSignature,
} from '@twilio-labs/serverless-runtime-types/types';

type MyEvent = {
  PlaceId: string
}

type MyContext = {
  GCP_KEY: string
}

export const handler: ServerlessFunctionSignature<MyContext, MyEvent> = async function(
  context: MyContext,
  event: MyEvent,
  callback: ServerlessCallback
) {
  console.log(`Executing is-open`, event);
  
  var request = {key: context.GCP_KEY};
  var client = new Client();

//Get the place details for the given place_id using the google Javascript SDK  
var place = await client.placeDetails({params:{...request, place_id: event.PlaceId}});
  
  console.log(place.data?.result?.opening_hours);
  if(place.data?.result?.opening_hours?.periods){
    place.data?.result?.opening_hours?.periods.forEach(el => {
      console.log(el);
    })
  }

  const openHours = place.data?.result?.opening_hours;
  
  callback(null, {open: openHours});
};

Note that within the code you make a request to the Google Places API with our Google Cloud Platform API key as a parameter. To use this you need to add this as a variable in the .env file at the root of your project. Your .env file will also require one more parameter to allow for you to deploy this project at a later stage. So with that, open up your .env file and paste the following (remembering to replace the placeholder values with your own).

ACCOUNT_SID=#YOUR_ACCOUNT_SID#
AUTH_TOKEN=#YOUR_AUTH_TOKEN#
GCP_KEY=#YOUR_GCP_KEY#

With this change, you are now ready to test your serverless function.

To start the project you can run the following command in your shell:

npm run start

This will start the project running on your localhost.

For the next part, you will need Google’s “Place ID” for your business and you will require your business to have opening hours listed on Google. The easiest way to retrieve the Place ID is by using Google’s Place ID Finder tool 

  1. Head over to Google’s Place ID Finder
  2. Search for your business in the search box and select the correct result (do not click on your businesses pin on the map)
  3. Take a note of the place ID

Google Maps screenshot showing Place ID finder for Twilio&#x27;s London office.

With this, you can open a browser and browse to http://localhost:3000/get-open-hours?PlaceId=#YOUR_PLACE_ID#. If everything is working correctly, you will see something similar to below:

Output from the function showing opening times in JSON format.

It’s now time for you to deploy the project to the Twilio platform. The serverless CLI tools make this easy by allowing you to carry this out with a single command:

npm run deploy

Providing this is successful you should see an output similar to below:

Console output including "Serverless project successfully deployed"

That’s it, you now have our Serverless Function in place that authenticates your requests, calls the Google Places API and returns data back to be used within a Studio Flow.

Studio Build

Now that you have the data that is needed and you have exposed via your Serverless Function, you are ready to build your Twilio Studio Flow.  This is where we will make a request to the function with your business’s place ID and then make decisions based on whether the business is open or closed.

Firstly, create a studio Flow by navigating to the “Studio > Flows” section of the Twilio Console and pressing the + button. Once you’ve given your new Flow a name, you will be presented with this:

Twilio Studio "New Flow" wizard. "Start from scratch" is selected.

Scroll down to “Import from JSON” and hit next. On the next screen is where you can paste the following JSON

{
  "description": "A New Flow",
  "states": [
    {
      "name": "Trigger",
      "type": "trigger",
      "transitions": [
        {
          "event": "incomingMessage"
        },
        {
          "next": "check-open",
          "event": "incomingCall"
        },
        {
          "event": "incomingRequest"
        },
        {
          "event": "incomingParent"
        }
      ],
      "properties": {
        "offset": {
          "x": 0,
          "y": 0
        }
      }
    },
    {
      "name": "check-open",
      "type": "run-function",
      "transitions": [
        {
          "next": "split-open",
          "event": "success"
        },
        {
          "event": "fail"
        }
      ],
      "properties": {
        "service_sid": "",
        "environment_sid": "",
        "offset": {
          "x": 120,
          "y": 180
        },
        "function_sid": "",
        "parameters": [
          {
            "value": "#YOUR_BUSINESS_PLACE_ID#",
            "key": "PlaceId"
          }
        ]
      }
    },
    {
      "name": "open",
      "type": "say-play",
      "transitions": [
        {
          "event": "audioComplete"
        }
      ],
      "properties": {
        "voice": "alice",
        "offset": {
          "x": 460,
          "y": 750
        },
        "loop": 1,
        "say": "The business is open",
        "language": "en-US"
      }
    },
    {
      "name": "closed",
      "type": "say-play",
      "transitions": [
        {
          "event": "audioComplete"
        }
      ],
      "properties": {
        "voice": "alice",
        "offset": {
          "x": -50,
          "y": 780
        },
        "loop": 1,
        "say": "The business is closed",
        "language": "en-US"
      }
    },
    {
      "name": "split-open",
      "type": "split-based-on",
      "transitions": [
        {
          "next": "closed",
          "event": "noMatch"
        },
        {
          "next": "closed",
          "event": "match",
          "conditions": [
            {
              "friendly_name": "If value equal_to open",
              "arguments": [
                "{{widgets.check-open.parsed.open.open_now}}"
              ],
              "type": "equal_to",
              "value": "false"
            }
          ]
        },
        {
          "next": "open",
          "event": "match",
          "conditions": [
            {
              "friendly_name": "If value equal_to open",
              "arguments": [
                "{{widgets.check-open.parsed.open.open_now}}"
              ],
              "type": "equal_to",
              "value": "true"
            }
          ]
        }
      ],
      "properties": {
        "input": "{{widgets.check-open.parsed.open.open_now}}",
        "offset": {
          "x": 150,
          "y": 410
        }
      }
    }
  ],
  "initial_state": "Trigger",
  "flags": {
    "allow_concurrent_calls": true
  }
}

This will build the new Studio Flow for you and then display it as shown below:

Screenshot of the Twilio Studio UI for the flow created using the JSON above.

You will notice that the check-open widget is blank, this is because you will need to configure this widget to call your specific serverless function. Do this by selecting the widget with a click and entering the following details in the “Config” section


Configuration for the "Run Function" widget

Don’t forget to replace the placeholder in the “Function Parameter” with your business’s PlaceId in the parameters section

Hit “Publish” on your Studio Flow and you are ready to test. To do this, use an existing phone number, or purchase a new one in the “Phone Numbers” section of the Twilio Console. Once purchased, you can use the “When a call comes in” configuration on the number page to trigger your Studio Flow.

Configuring a phone number to use the Studio Flow created above when a call comes in.

Once you hit “Save”, you will be able to call your configured phone number and hear whether your business is open or closed.

Coming Together

In this post, you have seen how the power of the integrations that Twilio Studio includes has enabled you to enrich your IVR with data that is held in a central system, potentially even managed by another team, therefore taking the burden off of your team to change your IVR when a company holiday occurs or something similar.

Can you enhance this solution? There are a lot more data points that come back from the Google Places API request you are making. Maybe you can tell the caller when you are next open, or adapt this into a chatbot where you send in detailed weekly opening hours to your customer.

You can see the whole code from this post, including the Studio Flow JSON in this github repo.

We can’t wait to see what you build!

Jordan is a Senior Solutions Engineer helping Twilio customers in the UK and Ireland to unlock the power of Twilio APIs and Software. Get in touch with by email jhanley@twilio.com