Integrate the Reminders API with Your Skill to Deepen Customer Engagement
Pan Wangperawong Aug 09, 2019
Share:
Tutorial Tips & Tools
Blog_Header_Post_Img

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.

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:

  1. Name the skill Banana Stand
  2. Choose Custom for skill type
  3. Choose Alexa-hosted for skill back end
  4. Click Create skill

Alexa Blog

Enable Reminders Permission

  1. Select Permissions on the bottom left menu

Alexa Blog

2. Scroll down to Reminders and toggle the switch on to enable it for the skill

permissions image

Update Packages

Click on Code and open package.json and update the packages to the latest versions:

  1. Click on the Code tab
  2. Open package.json
  3. Update the following packages
    1. Update ask-sdk-core to the latest version ("ask-sdk-core": "^2.6.0" when this tutorial was published)
    2. Add Moment Timezone package ("moment-timezone": "^0.5.25" when this tutorial was published) for generating and manipulating ISO 8601 timestamps
  4. Click Deploy

package.json image

Update LaunchIntentHandler

  1. Click on the Code tab
  2. Scroll to LaunchRequestHandler
  3. Update the speechText to the following:

Copied to clipboard
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

  1. 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
  2. Click on the Code tab and rename HelloWorldIntentHandler to CreateReminderIntentHandler:

Copied to clipboard
const CreateReminderIntentHandler = {
  ... // handler code
}

3. Update the canHandle function for CreateReminderIntentHandler to check for CreateReminderIntent with the following:

Copied to clipboard
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
  }
}

4. Go to the return exports.handler at the bottom of the code and update HelloWorldIntentHandler to CreateReminderIntentHandler:

Copied to clipboard
return exports.handler = Alexa.SkillBuilders.custom()
                                  .addRequestHandlers(
                                    ... // other intent handlers
                                    /* register CreateReminderIntentHandler */
                                    CreateReminderIntentHandler,
                                    )
                                  .addErrorHandlers(ErrorHandler)
                                  .lambda()

Create an Instance of theRemindersManagementServiceClient:

  1. Delete all the code in the handle function
  2. At exports.handler after addErrorHandlers(ErrorHandler) add withApiClient(new Alexa.DefaultApiClient()):

Copied to clipboard
return exports.handler = Alexa.SkillBuilders.custom()
                                ... // RequestHandlers
                                .addErrorHandlers(ErrorHandler)
                                /* add API Client Builder */
                                .withApiClient(new Alexa.DefaultApiClient())
                                .lambda()

3. Create an instance of remindersClient to interface with the Reminders API for creating a reminder:

Copied to clipboard
handle(handlerInput) { 
  const remindersApiClient = handlerInput.serviceClientFactory.getReminderManagementServiceClient()
}

Check If User Has Granted the Skill Permission to Send Reminders

  1. To declare and set the permissions variable we will use the ES6 destructor assignment syntax:

Copied to clipboard
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
}

2. Check if the user has granted permission for the skill to send reminders. If not, provide the user with an AskForPermissionsConsent consent

Copied to clipboard
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()
  }
}

3. Click Deploy and test skill on a device

Create a One-Time Reminder

  1. 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.

Copied to clipboard
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"
    }
  }
}

2. Let’s use the reminderRequest object and the remindersApiClient to schedule a reminder 20 seconds from now.

Copied to clipboard
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();
}

3.Click Deploy.

4. Test the skill on an Alexa-enabled device by saying “Alexa, open my banana stand” (reminders cannot be tested through the simulator).

5. 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 with async:

Copied to clipboard
async handle(handlerInput) {
  ... // handle function declared variables and code
}

  • Now prepend await to remindersApiClient.createReminder(reminderRequest) as such

Copied to clipboard
async handle(handlerInput) {
  ... // handle function declared variables and code
  await remindersApiClient.createReminder(reminderRequest)
}

  • To do error handling, let’s wrap await remindersApiClient.createReminder(reminderRequest) with a try-catch

Copied to clipboard
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();
  }
}

Create a Recurring Reminder

  1. Import the moment-timezone package at the top of the file after const Alexa = require("ask-sdk-core")

Copied to clipboard
const Alexa = require("ask-sdk-core"),
    moment = require('moment-timezone') // Add moment-timezone package

2. 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 the Z at the end of the timestamp. Also, to obtain the user’s timezone you can use the Alexa Settings API

Copied to clipboard
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
}

3. 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:

Copied to clipboard
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