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).
How do Reminders Work?
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.
<br />
Initialize a New Skill Project Using the Hello World Example
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:
- Name the skill Banana Stand
- Choose Custom for skill type
- Choose Alexa-hosted for skill back end
- Click Create skill
Enable Reminders Permission
- Select Permissions on the bottom left menu
- Scroll down to Reminders and toggle the switch on to enable it for the skill
Update Packages
Click on Code and open package.json
and update the packages to the latest versions:
- Click on the Code tab
- Open
package.json
- Update the following packages
- Update
ask-sdk-core
to the latest version ("ask-sdk-core": "^2.6.0"
when this tutorial was published) - Add Moment Timezone package (
"moment-timezone": "^0.5.25"
when this tutorial was published) for generating and manipulating ISO 8601 timestamps
- Update
- Click Deploy
Update LaunchIntentHandler
- Click on the Code tab
- Scroll to
LaunchRequestHandler
- Update the
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?"
Add CreateReminderIntentHandler
- Click on the Build tab and select the HelloWorldIntent
- Delete
HelloWorldIntent
- Add a new intent
- Select Use an existing intent from Alexa's built-in library
- Search for
AMAZON.YesIntent
- Add sample utterances by clicking on the Sample Utterances section
- Click on Build Model to train the model based on what you added
- Delete
- Click on the Code tab and rename
HelloWorldIntentHandler
toCreateReminderIntentHandler
:const CreateReminderIntentHandler = { ... // handler code }
- Update the
canHandle
function forCreateReminderIntentHandler
to check forCreateReminderIntent
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 } }
- Go to the
return exports.handler
at the bottom of the code and updateHelloWorldIntentHandler
toCreateReminderIntentHandler
:return exports.handler = Alexa.SkillBuilders.custom() .addRequestHandlers( ... // other intent handlers /* register CreateReminderIntentHandler */ CreateReminderIntentHandler, ) .addErrorHandlers(ErrorHandler) .lambda()
Create an Instance of theRemindersManagementServiceClient:
- Delete all the code in the
handle
function - At
exports.handler
afteraddErrorHandlers(ErrorHandler)
addwithApiClient(new Alexa.DefaultApiClient())
:return exports.handler = Alexa.SkillBuilders.custom() ... // RequestHandlers .addErrorHandlers(ErrorHandler) /* add API Client Builder */ .withApiClient(new Alexa.DefaultApiClient()) .lambda()
- Create an instance of
remindersClient
to interface with the Reminders API for creating a reminder:handle(handlerInput) { const remindersApiClient = handlerInput.serviceClientFactory.getReminderManagementServiceClient() }
Check If User Has Granted the Skill Permission to Send Reminders
- To declare and set the
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 }
- Check if the user has granted permission for the skill to send reminders. If not, provide the user with an AskForPermissionsConsent consent
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() } }
- Click Deploy and test skill on a device
Create a One-Time Reminder
- Declare the reminder request object that uses
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" } } }
- Let’s use the
reminderRequest
object and theremindersApiClient
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(); }
- Click Deploy.
- Test the skill on an Alexa-enabled device by saying “Alexa, open my banana stand” (reminders cannot be tested through the simulator).
- Now let’s utilize the JavaScript ES6 async-await and try-catch pattern to better handle any potential errors when using the
remindersApiClient
to make an asynchronous HTTP request to schedule a reminder.- To use the async-await pattern to synchronize the asynchronous HTTP request and response start by prepending the
handle
function withasync
:async handle(handlerInput) { ... // handle function declared variables and code }
- Now prepend await to
remindersApiClient.createReminder(reminderRequest)
as suchasync handle(handlerInput) { ... // handle function declared variables and code await remindersApiClient.createReminder(reminderRequest) }
- To do error handling, let’s wrap
await remindersApiClient.createReminder(reminderRequest)
with atry-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(); } }
- To use the async-await pattern to synchronize the asynchronous HTTP request and response start by prepending the
Create a Recurring Reminder
- Import the
moment-timezone
package at the top of the file afterconst Alexa = require("ask-sdk-core")
const Alexa = require("ask-sdk-core"), moment = require('moment-timezone') // Add moment-timezone package
- Update reminder request object to use
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 theZ
at the end of the timestamp. Also, to obtain the user’s timezone you can use the Alexa Settings APIconst 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 }
- Since we cannot wait around until tomorrow to get this reminder, let's set the daily reminder to start today and have the first reminder be in 20 seconds for example sake. Update the
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 }
Conclusion
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.
Related Content
- Alexa Reminders API Overview
- Alexa Reminders Guidelines for Usage
- Alexa Reminders API Reference
- Alexa Reminders API Best Practices
- Calling Alexa Service APIs
- Reminder Management Service Client
- Example Reminders API Skill on GitHub
- Moment.js Timezone
- Obtain User’s Timezone with Alexa Settings API
- Setting Up an Alexa-hosted Skill Video
- Live Code Along Video - Integrate the Reminders API with Your Skill to Deepen Customer Engagement
Source: Alexa Developer Blog