Alexa Lists

Learn how to use the Alexa Skill Lists feature with Jovo.

Introduction

Alexa supports the ability for users to get list items and change them. Learn more in the official Alexa documentation.

Jovo offers methods to get items from a list. For example, you can get the item like this in a handler:

async someHandler() {
  const listId = '<LIST ID>';
  const itemId = '<ITEM ID>';

  try {
    const item = await this.$alexa!.$user.getListItem(listId, itemId);
    // ...
    return this.$send({ message: 'Processed item update'});
  } catch(error: Error) {
      // ...
  }
},

The item you receive from the method call is the same as defined in the official Alexa documentation.

Learn more in the following sections:

Permissions

You need to add permissions to your skill manifest as well as ask the users for permission during the interaction.

Add List-Read Permission to the Skill Manifest

To be able to get the items from a user-list, you need to add the permission to the Skill project

While you can manually enable the permission in the Alexa developer console, we recommend adding it to the skill.json manifest directly using the files property in the Alexa project config:

const project = new ProjectConfig({
  // ...
  plugins: [
    new AlexaCli({
      // ...
      files: {
        'skill-package/skill.json': {
          manifest: {
            permissions: [{ name: 'alexa::household:lists:read' }],
          },
        },
      },
    }),
  ],
});

Learn more about the permissions field in the official Alexa documentation.

Ask for Permission

Voice permissions provide a frictionless way to ask users if they want to provide access to their lists. Learn more in the official Alexa docs.

You can use the AskForListReadPermissionOutput (which extends AskForPermissionOutput) for this:

import { AskForListReadPermissionOutput } from '@jovotech/platform-alexa';
// ...

someHandler() {
  // ...

  return this.$send(AskForListReadPermissionOutput, {
    message: 'Please grant the permission to your list.',
  });
}

Receive Item Update Requests from Alexa

Your skill will also be called from Alexa when you subscribe to specific events. Learn more in the official Alexa docs.

First you have to add householdList to your used apis. You can't specify this using the Alexa console, that's why you have to enable it using the skill.json. The below example does that by using the files property in the Alexa project configuration:

const project = new ProjectConfig({
  // ...
  plugins: [
    new AlexaCli({
      // ...
      files: {
        'skill-package/skill.json': {
          manifest: {
            apis: {
              householdList: {},
            },
          },
        },
      },
    }),
  ],
});

Now you can tell Alexa which events you want to listen for. Find more information about events in the official Alexa docs. You also have to use the skill.json here:

const project = new ProjectConfig({
  // ...
  plugins: [
    new AlexaCli({
      // ...
      files: {
        'skill-package/skill.json': {
          manifest: {
            events: {
              endpoint: {
                sslCertificateType: 'Wildcard',
                uri: '${JOVO_WEBHOOK_URL}',
              },
              subscriptions: [
                {
                  eventName: 'ITEMS_CREATED',
                },
                {
                  eventName: 'ITEMS_UPDATED',
                },
                {
                  eventName: 'ITEMS_DELETED',
                },
              ],
            },
          },
        },
      },
    }),
  ],
});

You can, for example, use the following handlers to receive event requests from Alexa:

// ITEMS CREATED
@Handle({
  global: true,
  types: ['AlexaHouseholdListEvent.ItemsCreated'],
  platforms: ['alexa'],
})
handleCreatedItems() {
  const { listId, listItemIds } = this.$alexa?.$request.request?.body;
  console.log(`Added ${listItemIds} to ${listId}`);

  if (!listItemIds || !listId) {
    return;
  }
  Promise.all(listItemIds.map(itemId => this.$alexa?.$user?.getListItem(listId, itemId)))
      .then(result => console.log('The created items are: ', result))
}

// ITEMS UPDATED
@Handle({
  global: true,
  types: ['AlexaHouseholdListEvent.ItemsUpdated'],
  platforms: ['alexa'],
})
async handleUpdatedItems() {
  const { listId, listItemIds } = this.$alexa?.$request.request?.body;
  console.log(`Modified ${listItemIds} from ${listId}`);
}

// ITEMS DELETED
@Handle({
  global: true,
  types: ['AlexaHouseholdListEvent.ItemsDeleted'],
  platforms: ['alexa'],
})
async handleDeletedItems() {
  const { listId, listItemIds } = this.$alexa?.$request.request?.body;
  console.log(`Deleted ${listItemIds} from ${listId}`);
}

List Management Methods

The following methods can be used to call the Alexa List Management API

Get Lists

The getLists() method can be used to make an API call to the lists metadata endpoint in the Alexa List Management API:

async someHandler() {
  try {
    const lists = await this.$alexa!.$user.getLists();
    // ...
  } catch(error: Error) {
      // ...
  }
},

This returns an array of the following structure:

[
  {
    "listId": "MTIzLXRvLWRvLVRBU0s=",
    "name": "Alexa to-do list",
    "state": "active",
    "version": 1,
    "statusMap": [{
            "href": "URL",
            "status": "active"
        },
        {
            "href": "URL",
            "status": "completed"
        }
    ]
  },
  // ...
]

Get Items From a List

The getListItem() and getListItems() methods can be used to make an API call to the list item endpoint in the Alexa List Management API:

await this.$alexa.$user.getListItem(listId: string, itemId: string) // Retrieve a single item
await this.$alexa.$user.getListItems(listId: string, itemIds: string[]) // Retrieve multiple items

// Example
async someHandler() {
  const listId = '<LIST ID>';
  const itemId = '<ITEM ID>';

  try {
    const item = await this.$alexa!.$user.getListItem(listId, itemId);
    // ...
    return this.$send({ message: 'Processed item update'});
  } catch(error: Error) {
      // ...
  }
},