Introduction
In this article on custom Alexa skills, I am going to demonstrate how to get a user’s mobile number for use in a custom Alexa skill on Alexa Hosted node.js server. For that, I will be creating a custom Alexa skill named user information. With the help of this skill, Users can ask Alexa for a user’s mobile number to be used in custom Alexa skill and Alexa fetches a user’s mobile number from the Amazon account linked with that particular Alexa skill and returns the mobile number as part of the information about the user.
The users can ask Alexa for the mobile number by saying some sample utterances such as “what is my mobile number”, Alexa will respond by fetching and prompting the user’s mobile number 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 to the Alexa developer console. We then 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 MobileIntent along with some sample utterances such as mobile number, what’s my mobile number, and what is my mobile number. 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": "MobileIntent",
- "slots": [],
- "samples": [
- "mobile",
- "phone",
- "mobile number",
- "what is my mobile number",
- "what is my phone number"
- ]
- }
- ],
- "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 MOBILE_PERMISSION = "alexa::profile:mobile_number:read";
-
- const LaunchRequestHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'LaunchRequest';
- },
- handle(handlerInput) {
-
- const speechText = `Hello. You can say: what's my mobile number.`;
-
- const reprompt = `say: what's my mobile number.`;
-
- return handlerInput.responseBuilder
- .speak(speechText)
- .reprompt(reprompt)
- .getResponse();
- },
- };
-
- const MobileIntentHandler = {
- canHandle(handlerInput) {
- return handlerInput.requestEnvelope.request.type === 'IntentRequest'
- && handlerInput.requestEnvelope.request.intent.name === 'MobileIntent';
- },
- async handle(handlerInput) {
- const { serviceClientFactory, responseBuilder } = handlerInput;
- try {
- const upsServiceClient = serviceClientFactory.getUpsServiceClient();
- const profileMobileObject = await upsServiceClient.getProfileMobileNumber();
- if (!profileMobileObject) {
- const errorResponse = `It looks like you are not having a mobile number set. You can set your mobile number from the companion app.`
- return responseBuilder
- .speak(errorResponse)
- .getResponse();
- }
- const profileMobile = profileMobileObject.phoneNumber;
- const speechResponse = `Your mobile number is, ${profileMobile}`;
-
- return responseBuilder
- .speak(speechResponse)
- .getResponse();
- } catch (error) {
- console.log(JSON.stringify(error));
- if (error.statusCode === 403) {
- return responseBuilder
- .speak(messages.NOTIFY_MISSING_PERMISSIONS)
- .withAskForPermissionsConsentCard([MOBILE_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,
- MobileIntentHandler,
- 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 mobile number” to fetch and prompt the user’s mobile number.
MobileIntentHandler
After that, the MobileIntent Handler is defined to handle each and every user's request to ask Alexa to fetch and provide the user’s mobile number, 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 mobile number so that Alexa can fetch and provide user’s mobile number without having to ask for permission to access the user’s mobile number.
As we can see from the above code, inside the handle() function of MobileIntentHandler, 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 getProfileMobileNumber() method inside UpsServiceClient class returns string containing user’s mobile number.
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 to request the customer's permission to their contact information, the following steps needs 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 the Customer mobile number.
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 the mobile number is alexa::profile:mobile_number:read.
The permission 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, a 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 is my mobile number, so that Alexa can fetch and provide the user’s mobile number.
If a user responds through a sample utterance that is by saying “what is my mobile number”, then Alexa will fetch and provide the user’s mobile number.
If this skill has permission to access the user’s mobile number then Alexa will return a string containing the user’s mobile number. If this skill does not have any permission to access the user’s mobile number then Alexa will prompt a message saying “Please enable profile permissions in 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 mobile number for use in a custom Alexa skill and how Alexa fetches a user’s mobile number from the amazon account linked with that particular Alexa skill and returns the mobile number as part of information about the user. Proper coding snippets along with the output for the backend of the skill is also provided.