Introduction
In this article on custom Alexa skills, I am going to demonstrate how to get a user’s email address for use in a custom Alexa skill on Alexa Hosted node.js server. For that, I will create a custom Alexa skill named user information. With the help of this skill, Users can ask Alexa for a user’s email address to be used in custom Alexa skill and Alexa fetches the user’s email address from the amazon account linked with that particular Alexa skill and returns the email address as part of the information about the user.
The users can ask Alexa for the email address by saying some sample utterances such as “what is my email address”, Alexa will respond by fetching and prompting the user’s email address accordingly. Alexa can also respond by prompting please enable profile permissions from the Amazon Alexa app. The backend code required for the Alexa skill to work will be stored in a lambda function inside the Amazon developer console.
Creating Alexa Skill
To create a new skill, first, we need to login into the Alexa developer console, we need to mention the unique skill name and select the default language according to our location.
After that, we can choose a model to add to our skill. To create a custom skill, we can select custom model.
We can also choose a method or a template to host the skill’s backend code inside a lambda function.
We can choose Alexa hosted node.js or python template. We can also mention our own endpoint or server to store backend resources for the required Alexa skills.
The next step is to choose a template to add to our skill, which we customize later according to our need and click on the create a skill button.
Now as a skill has been created, we need to make adjustments to the skill’s frontend. Now I will be creating intents, slots, and custom slot types to create skill’s frontend. First, we need to mention the invocation name. Users say a skill's invocation name to begin an interaction with a particular custom skill.
Now we have to create intents.
Here, I have added a new intent named EmailIntent along with some sample utterances such as email address, what’s my email address, and what is my email. No slots and custom slot types are defined for this Alexa skill.
After creating a model for a particular skill, we can save and build the model by clicking on the save model and build model button on the top.
Json code for the above frontend is as follows:
- {
- "interactionModel": {
- "languageModel": {
- "invocationName": "contact info",
- "intents": [
- {
- "name": "AMAZON.NavigateHomeIntent",
- "samples": []
- },
- {
- "name": "AMAZON.CancelIntent",
- "samples": []
- },
- {
- "name": "AMAZON.HelpIntent",
- "samples": []
- },
- {
- "name": "AMAZON.StopIntent",
- "samples": []
- },
-
- {
- "name": "EmailIntent",
- "slots": [],
- "samples": [
- "email",
- "what is my email",
- "what's my email address"
- ]
- },
-
- ],
- "types": []
- }
- }
- }
Creating the backend resource for the Alexa skill
To create backend code inside the lambda function, we can write code inside the index.js node.js file. The code for the custom Alexa skill is as follows:
- const Alexa = require('ask-sdk-core');
-
- const messages = {
- NOTIFY_MISSING_PERMISSIONS: 'Please enable profile permissions in the Amazon Alexa app.',
- ERROR: 'Uh Oh. Looks like something went wrong.'
- };
-
- const EMAIL_PERMISSION = "alexa::profile:email:read";
-
-
- const LaunchRequestHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
- },
- handle(handlerInput) {
-
- const speechText = `Hello. You can say: what's my email address.`;
-
- const reprompt = `say: what's my email address.`;
-
- return handlerInput.responseBuilder
- .speak(speechText)
- .reprompt(reprompt)
- .getResponse();
- },
- };
-
- const EmailIntentHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest'
- && handlerInput.requestEnvelope.request.intent.name === 'EmailIntent';
- },
- async handle(handlerInput) {
- const { serviceClientFactory, responseBuilder } = handlerInput;
- try {
- const upsServiceClient = serviceClientFactory.getUpsServiceClient();
- const profileEmail = await upsServiceClient.getProfileEmail();
- if (!profileEmail) {
- const noEmailResponse = `It looks like you are not having an email set. You can set your email from the companion app.`
- return responseBuilder
- .speak(noEmailResponse)
- .getResponse();
- }
- const speechResponse = `Your email is, ${profileEmail}`;
- return responseBuilder
- .speak(speechResponse)
- .getResponse();
- } catch (error) {
- console.log(JSON.stringify(error));
- if (error.statusCode === 403) {
- return responseBuilder
- .speak(messages.NOTIFY_MISSING_PERMISSIONS)
- .withAskForPermissionsConsentCard([EMAIL_PERMISSION])
- .getResponse();
- }
- console.log(JSON.stringify(error));
- const response = responseBuilder.speak(messages.ERROR).getResponse();
- return response;
- }
- },
- }
-
- },
- }
-
- const HelpIntentHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest'
- && handlerInput.requestEnvelope.request.intent.name === 'AMAZON.HelpIntent';
- },
- handle(handlerInput) {
- const speechText = 'You can say hello to me!';
-
- return handlerInput.responseBuilder
- .speak(speechText)
- .reprompt(speechText)
- .withSimpleCard('Hello World', speechText)
- .getResponse();
- },
- };
-
- const CancelAndStopIntentHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest'
- && (handlerInput.requestEnvelope.request.intent.name === 'AMAZON.CancelIntent'
- || handlerInput.requestEnvelope.request.intent.name === 'AMAZON.StopIntent');
- },
- handle(handlerInput) {
- const speechText = 'Goodbye!';
-
- return handlerInput.responseBuilder
- .speak(speechText)
- .getResponse();
- },
- };
-
- const SessionEndedRequestHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'SessionEndedRequest';
- },
- handle(handlerInput) {
- console.log(`Session ended with reason: ${handlerInput.requestEnvelope.request.reason}`);
-
- return handlerInput.responseBuilder.getResponse();
- },
- };
-
- const ErrorHandler = {
- canHandle() {
- return true;
- },
- handle(handlerInput, error) {
- console.log(`Error handled: ${error.message}`);
-
- return handlerInput.responseBuilder
- .speak('Sorry, I can\'t understand the command. Please say again.')
- .reprompt('Sorry, I can\'t understand the command. Please say again.')
- .getResponse();
- },
- };
-
-
- const skillBuilder = Alexa.SkillBuilders.custom();
-
- exports.handler = skillBuilder
- .addRequestHandlers(
- LaunchRequestHandler,
- EmailIntentHandler,
- HelpIntentHandler,
- CancelAndStopIntentHandler,
- SessionEndedRequestHandler
- )
-
- .addErrorHandlers(ErrorHandler)
- .withApiClient(new Alexa.DefaultApiClient())
- .lambda();
To receive request from the user, request handlers are created for each intent to handle. Inside each handlers, canHandle and handle functions are defined.
The canHandle() function is where you define what requests the handler responds to. The handle() function returns a response to the user. If your skill receives a request, the canHandle() function within each handler determines whether or not that handler can service the request.
LaunchRequestHandler
In this case, the user wants to launch the skill, which is a LaunchRequest. Therefore, the canHandle() function within the LaunchRequestHandler will let the SDK know it can fulfill the request. In computer terms, the canHandle returns true to confirm it can do the work.
With the help of the handle() function inside LaunchRequestHandler, Alexa greets a user by saying “Hello” and tells the user to say “what is my email address” to fetch and prompt the user’s email address.
EmailIntentHandler
After that EmailIntent Handler is defined to handle each and every user's request to ask Alexa to fetch and provide the user’s email address whosoever has to enable this particular skill on their Amazon Alex app.
This handler will configure the skill to ask a user for permission to read his/her email address so that Alexa can fetch and provide the user’s email address without having to ask for permission to access the user’s name.
As we can see from the above code, inside handle() function of EmailIntentHandler. The ServiceClientFactory is available to the handlers via the HandlerInput container object. It takes care of creating individual service client and configuring the ApiAccessToken and ApiEndpoint.
UpsServiceClient
UpsServiceClient can be used to query Alexa Customer Profile API for customer contact information and Alexa Customer Settings API for customer preferences of time zone, distance measuring, and temperature measurement unit.
The getProfileEmail() method inside UpsServiceClient class returns string containing user’s email address.
Request Customer Contact Information for Use in Your Skill
When a customer enables your Alexa skill, your skill can request the customer's permission to their contact information, which includes name, email address, and phone number, if the customer has consented. You can then use this data to support personalized intents to enhance the customer experience without account linking.
To enable this skills capability, in order to request the customer's permission to their contact information the following steps need to be taken:
- If you are using the developer console to manage your skill, configure your skill as follows:
- Edit your skill in the developer console.
- Navigate to the Build -> Permissions page in the console.
- select Customer Email Address to be used in the skill.
Sample response with permissions card
To query the Alexa Customer Profile API for customer contact information permissions card for requesting customer consent is required to define. Particular skills can display a special permissions card to ask customers for consent dynamically.
An in-session interaction can return a response that includes the new AskForPermissionsConsent card.
The permission value for Email Address is alexa::profile:email:read
The permissions value always matches the scope that you declared for the skill on the Build -> Permissions page in the developer console.
Output
As we can see from the output above, to invoke a skill, the user can say open followed by invocation name. Here contact info is an invocation name to invoke this skill. Once the skill is invoked, Alexa greets the user by saying hello and tells the user to say what my email, so that Alexa can fetch and provide the user’s email address.
If a user responds through a sample utterance that is by saying “what is my email”, then Alexa will fetch and provide the user’s email.
If this skill has permission to access the user’s email then Alexa will return a string containing the user’s email. If this skill does not have any permission to access the user’s email then Alexa will prompt a message saying “Please enable profile permissions in the Amazon Alexa app”.
Summary
In this article, I created a custom Alexa skill. I also defined the invocation name, intents, and sample utterances. I demonstrated how to get a user’s email address for use in a custom Alexa skill and how Alexa fetches the user’s email address from the amazon account linked with that particular Alexa skill and returns the email address as part of the information about the user. Proper coding snippets along with the output for the backend of the skill is also provided.