Twilio Flex Schedule Manager
This solution provides a flexible, robust, and scalable way to manage open and closed hours for Twilio Flex applications.
Disclaimer
This software is to be considered "sample code", a Type B Deliverable, and is delivered "as-is" to the user. Twilio bears no responsibility to support the use or implementation of this software.
How it works
The schedule manager uses two main concepts: rules and schedules. Rules define a single or recurring event, such as "open Monday - Friday 8 AM - 5 PM," or "closed for holiday on 9/5/2022." Schedules are comprised of one or more rules (along with a time zone to apply the rules for). When checking the schedule from your application, such as a Studio flow, the status of the schedule will be returned (open or closed), and if it is closed, information from the matching rule (such as closed for holiday) will also be provided to allow for flexible handling.
To manage rules and schedules, a Flex plugin is provided which adds a Schedule Manager item to the side navigation for workers with the admin
role. This allows viewing the current configuration, the status of each schedule, and publishing updates to the configuration.
To allow for greater scalability than provided by Twilio Sync and some other solutions, configuration is stored within a Twilio Asset behind a Twilio Function. When updates to the configuration are being saved, a new asset version is generated and included in a new build, which is deployed when completed. This means that publishing schedules may take a few moments.
Installation
If you have performed the recommended steps to deploy the template, or followed the steps to deploy from your local machine, the schedule manager has already been deployed for you.
Details
Manual instructions for reference
First, switch to theaddons/serverless-schedule-manager
directory and install:cd addons/serverless-schedule-manager
npm install
Then, copy .env.example
to .env
and configure your Twilio account SID and token:
ACCOUNT_SID=ACxxxxxx
AUTH_TOKEN=abc123
TWILIO_SERVICE_RETRY_LIMIT=5
TWILIO_SERVICE_MIN_BACKOFF=100
TWILIO_SERVICE_MAX_BACKOFF=300
Finally, deploy the serverless functions:
npm run deploy
Note the domain name that is output when the deploy completes--this will be referenced throughout the rest of the readme.
Note: If you need to re-deploy via CLI in the future, be sure to first verify your local serverless-schedule-manager/assets/config.private.json
file contains any configuration changes made from the UI. This is done automatically by npm run deploy
if the service already exists, but it is recommended to first run npm run fetch-config
before attempting a re-deploy to ensure the latest file is present.
Then, update your flex-config ui_attributes file(s) with the serverless function domain from above:
{
"custom_data": {
"features": {
"schedule_manager": {
"enabled": true,
"serverless_domain": "my-serverless-domain.twil.io"
}
}
}
}
Example configurations
Example configurations, such as pre-defined holidays, are available in the examples
directory. To use one of these, paste the contents of the desired file into serverless-schedule-manager/assets/config.private.json
, replacing its contents. Then, deploy the service.
Evaluating schedules
You can evaluate a schedule in your application by making an HTTP request to the check-schedule
function:
https://my-serverless-domain/check-schedule?name=Schedule%20Name%20Here
or
POST https://my-serverless-domain/check-schedule
Content-Type: application/json
{
"name": "Name of the schedule you want to evaluate"
}
The schedule's rules will then be evaluated against the current date and time. The logic is as follows:
- If the schedule is marked as manually closed, the schedule is closed, and the closed reason provided is
manual
. - If one or more closed rules match, the schedule is closed, and the closed reason provided is from the topmost closed rule match in the list.
- If an open rule matches and no closed rules match, the schedule is open.
- If no rules match, the schedule is closed, and the closed reason provided is
closed
.
Here is an example response from a closed schedule:
{
"isOpen": false,
"closedReason": "closed"
}
Here is an example response from an open schedule:
{
"isOpen": true,
"closedReason": ""
}
Simulating schedules
When performing testing, you may want to simulate a date/time when evaluating a schedule. To do so, provide an ISO 8601-formatted date/time in the simulate
parameter in your request:
POST https://my-serverless-domain/check-schedule
Content-Type: application/json
{
"name": "Name of the schedule you want to evaluate",
"simulate": "2022-08-08T07:59"
}
Get the status of all schedules
Schedule data may be useful within Flex, such as during queue transfers. The included utility classes in the plugin component provide an easy way to get the status of all schedules:
import { loadScheduleData } from 'utils/schedule-manager';
...
const scheduleData = await loadScheduleData();
This calls the admin/list
function, which requires the Flex user token. scheduleData
will include a data
object with a schedules
array, containing each schedule. Each schedule will have a status
object, which contains the same object you would get from calling the check-schedules
function.
Using within Studio
If you deployed the template with the Deploy Terraform?
option selected, a Studio flow called Template Example Schedule Flow
has already been created for you, which you can hook up to a phone number in the Twilio Console. Otherwise, you can create your own:
- Bring a Run Function widget into your flow, named
check_schedule
, configured to theschedule-manager
service, and/check-schedule
function. - Add to the function parameters: key:
name
, value:Name of schedule to check
and save. - Bring a Split Based On... widget into your flow and set the variable to test:
widgets.check_schedule.parsed.isOpen
- Add a condition Equal To
true
, connect it to the step representing your open logic, and save. - Connect the 'No Condition Matches' to your closed logic.
- If you'd like to say different text depending on the closed reason, here is an example you can use in a Say/Play widget:
{% case widgets.check_schedule.parsed.closedReason %}
{% when 'manual' %}
We are currently closed due to unforseen circumstances. Please call back later.
{% when 'holiday' %}
We are currently closed due to the holiday. Please call back during our normal business hours.
{% else %}
We are currently closed. Please call back during our normal business hours.
{% endcase %}
Thank you for calling and have a great day.
Preventing conflicting configuration updates
The Flex plugin loads the configuration interface for workers with the admin
role, of which there may be more than one. Therefore, it is a possibility that multiple people may attempt to update the schedule configuration at the same time. To prevent workers overwriting each other's changes, a few guards have been put in place:
- When updating configuration with the
update-schedules
function, theversion
property must be provided with the sameversion
that was retrieved from thelist-schedules
function which loaded the initial data. If this does not match, the request will fail. In the user interface, the following alert will be shown:Schedule was updated by someone else and cannot be published. Please reload and try again.
This allows the worker to rescue the changes they were attempting to make, and merge them with the changes that were saved first. - When retrieving configuration from this
list-schedules
function, a check is made that the latest build is what is deployed. TheversionIsDeployed
property is returned indicating whether this is the case. If it is not, this means another user is in the middle of publishing changes. In the user interface, the following alert will be shown:Another schedule publish is in progress. Publishing now will overwrite other changes.
This allows the worker to wait for the publish to complete before making changes.
Development
Run twilio flex:plugins --help
to see all the commands we currently support. For further details on Flex Plugins refer to our documentation on the Twilio Docs page.