Integrating the Reminders API in your skill is a great way to actively extend its utility without requiring customers to launch your skill. With the Reminders API, you can engage more frequently with your customers and become a part of their routines.
In this tutorial, we will go over how to integrate the Reminders API into your skill. We'll be using Node.js and the Alexa Skills Kit (ASK) SDK. This tutorial uses an Alexa-hosted skill, but it can easily be adapted to use a local development process with the ASK Command-Line Interface (CLI).
Before we get started, let’s first go over the basics of how users can interact with a skill that utilizes the Reminders API, as well as how developers can interact with the Reminders API. To understand how users can interact with a skill that uses the Reminders API, consider the following interaction:
Alexa: Would you like to schedule a daily reminder at one p.m. to get a banana from the stand?
User: Sounds good.
In this example, a reminder will be scheduled at 1:00 p.m. and when it comes time, the Alexa-enabled device will chime and announce the reminder. If you have the Alexa mobile app and have push notifications enabled, it will send the reminder as a push notification as well.
For developers, there are two ways to interact with the Reminders API: in-session interactions and out-of-session interactions. For the purposes of this tutorial, we are only going to focus on in-session interactions.
In-session interactions allows a user interacting directly with the skill to create, read, update, and delete reminders. It’s important to note that reminders can only be created through in-session interactions, so it’s important to be clear and upfront about what customers can expect when they create the reminder, so they are not surprised by the outcome later. See our documentation for best practices integrating reminders in your skill and how to develop a good customer experience with reminders. If we recall the dialog shown earlier in this tutorial, a best practice is to specify frequency, time, and purpose.
Would you like to schedule a daily (frequency) reminder at one p. m. (time) to get a banana from the stand (purpose)?
Out-of-session interactions do not require a user to interact directly with a skill to read, update, and delete a reminder. These operations can be managed through the skill code on behalf of the user. However, out-of-session interactions cannot create a reminder. The functionality is limited to read, update, and delete. We are only focusing on in-session interactions in this tutorial, but if you’d like to learn more about out-of-session interactions, see the documentation.
Now that we've covered the basics of the Reminders API and best practices for using it, let's see how we can implement the code to create a reminder. You can follow along here, watch the video, or both.
Now we're going to create a basic Hello World skill, using the Alexa-hosted option for our back end. If you are already familiar with this process, you can skip to the next section. Otherwise you can watch a video on setting up an Alexa-hosted Skill or click on Create Skill and take the following steps:
Click on Code and open package.json
and update the packages to the latest versions:
package.json
ask-sdk-core
to the latest version ("ask-sdk-core": "^2.6.0"
when this tutorial was published)"moment-timezone": "^0.5.25"
when this tutorial was published) for generating and manipulating ISO 8601 timestampsLaunchRequestHandler
speechText
to the following:
const speechText = “Welcome to Banana Stand. Would you like a daily reminder at one p.m. to pickup a banana from the stand?"
HelloWorldIntent
AMAZON.YesIntent
HelloWorldIntentHandler
to CreateReminderIntentHandler
:
const CreateReminderIntentHandler = {
... // handler code
}
canHandle
function for CreateReminderIntentHandler
to check for CreateReminderIntent
with the following:
const CreateReminderIntentHandler = {
canHandle(handlerInput) {
return handlerInput.requestEnvelope.request.type === "IntentRequest" &&
/* Update it to check for AMAZON.YesIntent */
handlerInput.requestEnvelope.request.intent.name === "AMAZON.YesIntent";
},
handle(handlerInput) {
... // handler code
}
}
return exports.handler
at the bottom of the code and update HelloWorldIntentHandler
to CreateReminderIntentHandler
:
return exports.handler = Alexa.SkillBuilders.custom()
.addRequestHandlers(
... // other intent handlers
/* register CreateReminderIntentHandler */
CreateReminderIntentHandler,
)
.addErrorHandlers(ErrorHandler)
.lambda()
handle
functionexports.handler
after addErrorHandlers(ErrorHandler)
add withApiClient(new Alexa.DefaultApiClient())
:
return exports.handler = Alexa.SkillBuilders.custom()
... // RequestHandlers
.addErrorHandlers(ErrorHandler)
/* add API Client Builder */
.withApiClient(new Alexa.DefaultApiClient())
.lambda()
remindersClient
to interface with the Reminders API for creating a reminder:
handle(handlerInput) {
const remindersApiClient = handlerInput.serviceClientFactory.getReminderManagementServiceClient()
}
permissions
variable we will use the ES6 destructor assignment syntax:
handle(handlerInput) {
const remindersApiClient = handlerInput.serviceClientFactory.getReminderManagementServiceClient(),
/* Use ES6 destructor assignment syntax to declare and set permissions object in one step */
{ permissions } = handlerInput.requestEnvelope.context.System.user
}
handle(handlerInput) {
const apiClient = handlerInput.serviceClientFactory.getReminderManagementServiceClient(),
{ permissions } = handlerInput.requestEnvelope.context.System.user
/*
Check if user has granted the skill permissions.
If not, send consent card to request reminders read and write permission for skill
*/
if(!permissions) {
return handlerInput.responseBuilder
.speak("Please enable reminders permissions in the Amazon Alexa app")
.withAskForPermissionsConsentCard(["alexa::alerts:reminders:skill:readwrite"])
.getResponse()
}
}
SCHEDULED_RELATIVE
to schedule a one-time reminder for 20 seconds later. Read more about about constructing a request body that uses SCHEDULED_RELATIVE in the documentation.
handle(handlerInput) {
... // Continuing from the code we wrote to check if the customer has granted the reminders permission to the skill
/* Declare the reminderRequest object to make a request to schedule a reminder */
const reminderRequest = {
trigger: {
type: "SCHEDULED_RELATIVE",
offsetInSeconds: "20",
},
alertInfo: {
spokenInfo: {
content: [{
locale: "en-US",
text: "Time to get yo banana",
}],
},
},
pushNotification: {
status: "ENABLED"
}
}
}
reminderRequest
object and the remindersApiClient
to schedule a reminder 20 seconds from now.
handle(handlerInput) {
... // reminderRequest variable and other variables
/*
Use the remindersApiClient that was previously instantiated with
the reminderRequest object that specifies specifics of the reminder
to schedule a reminder
*/
remindersApiClient.createReminder(reminderRequest)
return handlerInput.responseBuilder
.speak("A reminder to get a banana in 20 seconds has been successfully created.")
.getResponse();
}
remindersApiClient
to make an asynchronous HTTP request to schedule a reminder.
handle
function with async
:
async handle(handlerInput) {
... // handle function declared variables and code
}
remindersApiClient.createReminder(reminderRequest)
as such
async handle(handlerInput) {
... // handle function declared variables and code
await remindersApiClient.createReminder(reminderRequest)
}
await remindersApiClient.createReminder(reminderRequest)
with a try-catch
async handle(handlerInput) {
... // handle function declared variables and code
try {
await remindersApiClient.createReminder(reminderRequest)
} catch(error) {
console.log("~~~~~ createReminder Error ${error} ~~~~~")
return handlerInput.responseBuilder
.speak("There was an error creating your reminder. Please let the skill publisher know.")
.getResponse();
}
}
moment-timezone
package at the top of the file after const Alexa = require("ask-sdk-core")
const Alexa = require("ask-sdk-core"),
moment = require('moment-timezone') // Add moment-timezone package
SCHEDULED_ABSOLUTE
and moment timezone package we installed at the beginning of the video to generate a timestamp that conforms with ISO 8601. Please note that the timestamp does not include the Z
at the end of the timestamp. Also, to obtain the user’s timezone you can use the Alexa Settings API
const CreateReminderIntentHandler = {
canHandle(handlerInput) {
... // can handle logic
},
async handle(handlerInput) {
... // Variable declarations
... // Code to check for permissions
const currentTime = moment().tz("America/Los_Angeles"), // Use Moment Timezone to get the current time in Pacific Time
reminderRequest = {
requestTime: currentTime.format("YYYY-MM-DDTHH:mm:ss"), // Add requestTime
trigger: {
type: "SCHEDULED_ABSOLUTE", // Update from SCHEDULED_RELATIVE
scheduledTime: currentTime.set({
hour: "13",
minute: "00",
second: "00"
}).format("YYYY-MM-DDTHH:mm:ss"),
timeZoneId: "America/Los_Angeles", // Set timeZoneId to Pacific Time
recurrence: {
freq : "DAILY" // Set recurrence and frequency
}
},
alertInfo: {
spokenInfo: {
content: [{
locale: "en-US",
text: "Time to get yo daily banana. You better go before the banistas pack up.",
}]
}
},
pushNotification: {
status: "ENABLED"
}
}
}
... // Code to create reminders
}
scheduledTime
to the following:
reminderRequest = {
requestTime: currentTime.format("YYYY:MM:DDTHH:mm:ss"),
trigger: {
type: "SCHEDULED_ABSOLUTE",
scheduledTime: currentTime.add(20, "seconds").format("YYYY-MM-DDTHH:mm:ss"),
... // other request parameters
}
... // other request parameters
}
In this tutorial, we used the Reminders API to create a skill that reminds users to pick up a banana. I think we can rest assured that we will be getting our daily banana! I hope you will take what we went over in this tutorial and adapt it to enhance your skill’s functionality and deepen engagement with your users.
We look forward to what you will build! Follow me on Twitter at @ItsPanW for more content like this and keep checking the Amazon Developer Blogs for updates.