Implement Alexa Shopping Actions in Your Skill

Your skill starts the Alexa Shopping flows by using the skill connections interface to hand off the purchase interaction to Alexa. Alexa Shopping tasks start a multi-turn interaction between Alexa and the customer to complete the shopping experience. When the task completes, Alexa invokes your skill to resume the session.

Follow these instructions to implement the hand off to Alexa and handle the session resume request. For an overview of Alexa Shopping, see Add Alexa Shopping Actions to Your Alexa Skill.

Product identity

Alexa Shopping Actions operate on a common ProductEntity object that represents a unique product within Amazon. When you invoke the API, you include an Amazon Standard Identification Number (ASIN) to identify the product. The ASIN is a 10-character alphanumeric unique identifier assigned by Amazon for product identification within Amazon.

You can find an ASIN for a product on the product detail page in two locations:

  1. In the URL for the product detail page in the format <Domain-Name>/dp/<asin>
  2. In the Product information section

The following table shows the definition of the ProductEntity object.

Field Description Type Required

asin

Amazon system identifier for a product.

String

Yes

Implement the add-to-cart flow

When you skill invokes the AMAZON.AddToShoppingCart action, Alexa presents the purchase details of the recommended product and asks for confirmation to add the product to the customer's Cart. The customer can review this item later by visiting the Amazon retail website. The customer doesn't make the purchasing decision during the skill session.

Complete the following steps to implement the Alexa Shopping AMAZON.AddToShoppingCart action in your skill code.

Send a directive to add a product to a shopping cart

After you ask the customer for permission to add the product to their Cart, you can send the Connections.StartConnection directive to Alexa with the AMAZON.AddToShoppingCart payload.

The following example shows a request to add the specified product to the cart.

Copied to clipboard.

/**
 * Handler to handle RecommendAndAddToCart.
 */
const RecommendAndAddToCartHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'RecommendAndAddToCart');
    },
    handle(handlerInput) {
        console.log("AmazoRecommendAndAddToCartreceived.");

        const speechText = "<Product intro>" +
        " Would you like to add this to your cart on Amazon?"

        return handlerInput.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .getResponse();
        }
};

/**
 * Handler to handle YesAndNoIntentHandler.
 */
const YesAndNoIntentHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'AMAZON.YesIntent'
                || request.intent.name === 'AMAZON.NoIntent');
    },
    handle(handlerInput) {
        const intentName = handlerInput.requestEnvelope.request.intent.name;
        console.log("User said : " + intentName);

        if (intentName === 'AMAZON.YesIntent') {
            var actionText = "Staging item with Amazon"
            let actionTask = {
              'type': 'Connections.StartConnection',
              'uri': 'connection://AMAZON.AddToShoppingCart/1',
              'input': {
                   'products' : [
                     {
                       'asin' : 'ASN1'
                     }
                   ]
                },
              'token': 'AddToShoppingCartToken'
          };

            return handlerInput.responseBuilder
            .speak(actionText)
            .addDirective(actionTask)
            .getResponse();
        }

        const speechText = "All right. Please come back if you need more product recommendations."

        return handlerInput.responseBuilder
            .speak(speechText)
            .getResponse();
        }
};

Handle a session resumed request

Your skill receives the result of the AMAZON.AddToShoppingCart request as a SessionResumedRequest response. Implement a handler to receive the response.

The response includes the token given in the AMAZON.AddToShoppingCart request. Use the token value to restore the previous session of the skill before you invoked the action. Resume the skill based on the result included in the payload. You can use the result to alter the skill experience and handle errors.

The following example shows how to handle a session resumed request response.

Copied to clipboard.

/**
 * Handler to handle SessionResumedRequest.
 */
const SessionResumedRequestHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'SessionResumedRequest';
    },
    handle(handlerInput) {

        // Session attributes and ID are same as previous IntentRequest
        const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
        console.log("sessionAttributes : " + JSON.stringify(sessionAttributes));
        const token = handlerInput.requestEnvelope.request.cause.token;

        let request = handlerInput.requestEnvelope.request;
        let speechText = "Sorry, I had trouble doing what you asked. Please try again.";

        if (request.cause) {
             const token = request.cause.token;
             const status = request.cause.status;
             const code = status.code;
             const message = status.message;
             const payload = request.cause.result;

             console.info(`[Shopping Response] ${JSON.stringify(request)}`);
             console.info(`[INFO] Shopping Action Result: Code - ${code}, Message - ${message}, Payload - ${payload}`);

             switch(code) {
                 case '200':
                     if (typeof payload !== "undefined") {
                         if (payload.code === "AlexaShopping.RetryLaterError") {
                             speechText =  "Looks like there was an issue. Let's get back to the skill.";
                         } else {
                             speechText = "I'm sorry, shopping indicated an issue while performing your request. Please try again later.";
                             console.info(`[INFO] Shopping Action had an issue while performing the request. ${payload.message}`);
                        }
                    } else if (token === "AddToShoppingCartToken") {
                        console.info(`[INFO] Shopping Action: Add to cart action was a success for ${token}.`);
                        speechText = "Thank you for adding the product to cart.";
                    }
            break;
            default :
                console.info(`[INFO] Shopping Action: There was a problem performing the shopping action.`);
                speechText = "There was a problem adding the item to your cart.";
            }
        }

        return handlerInput.responseBuilder.speak(speechText).getResponse();
    },
};

Implement the add-to-list flow

When you skill invokes the AMAZON.AddToList action, Alexa presents details of the recommended product and asks for confirmation to add the product to the customer's Amazon Wish List. The customer can review this item later by visiting the Amazon retail website. Customers can add items to the Wish List even if they don't define customer settings, such as payment method and delivery address.

Complete the following steps to implement the Alexa Shopping AMAZON.AddToList action in your skill code.

Send a directive to add a product to a list

After you ask the customer for permission to add the product to their Wish List, you can send the Connections.StartConnection directive to Alexa with the AMAZON.AddToList payload.

The following example shows a request to add the specified product to the list.

Copied to clipboard.

/**
 * Handler to handle RecommendAndAddToList.
 */
const RecommendAndAddToListHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'RecommendAndAddToList');
    },
    handle(handlerInput) {
        console.log("AmazoRecommendAndAddToCartreceived.");

        const speechText = "<Product intro>" +
        " Would you like to add this to your wish list on Amazon?"

        return handlerInput.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .getResponse();
        }
};

/**
 * Handler to handle YesAndNoIntentHandler.
 */
const YesAndNoIntentHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'AMAZON.YesIntent'
                || request.intent.name === 'AMAZON.NoIntent');
    },
    handle(handlerInput) {
        const intentName = handlerInput.requestEnvelope.request.intent.name;
        console.log("User said : " + intentName);

        if (intentName === 'AMAZON.YesIntent') {
            var actionText = "Staging item with Amazon"
            let actionTask = {
              'type': 'Connections.StartConnection',
              'uri': 'connection://AMAZON.AddToList/1',
              'input': {
                   'products' : [
                     {
                       'asin' : 'ASN1'
                     }
                 ],
                 "listType": "WISHLIST"
                },
              'token': 'AddToListToken'
          };

            return handlerInput.responseBuilder
            .speak(actionText)
            .addDirective(actionTask)
            .getResponse();
        }

        const speechText = "All right. Please come back if you need more product recommendations."

        return handlerInput.responseBuilder
            .speak(speechText)
            .getResponse();
        }
};

Handle a session resumed request

Your skill receives the result of the AMAZON.AddToList request as a SessionResumedRequest response. Implement a handler to receive the response.

The response includes the token given in the AMAZON.AddToList request. Use the token value to restore the previous session of the skill before you invoked the action. Resume the skill based on the result included in the payload. You can use the result to alter the skill experience and handle errors.

The following example shows how to handle a session resumed request response.

Copied to clipboard.

/**
 * Handler to handle SessionResumedRequest.
 */
const SessionResumedRequestHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'SessionResumedRequest';
    },
    handle(handlerInput) {

        // Session attributes and ID are same as previous IntentRequest
        const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
        console.log("sessionAttributes : " + JSON.stringify(sessionAttributes));
        const token = handlerInput.requestEnvelope.request.cause.token;

        let request = handlerInput.requestEnvelope.request;
        let speechText = "Sorry, I had trouble doing what you asked. Please try again.";

        if (request.cause) {
             const token = request.cause.token;
             const status = request.cause.status;
             const code = status.code;
             const message = status.message;
             const payload = request.cause.result;

             console.info(`[Add to List Response] ${JSON.stringify(request)}`);
             console.info(`[INFO] Shopping Action Result: Code - ${code}, Message - ${message}, Payload - ${payload}`);

             switch(code) {
                 case '200':
                     if (typeof payload !== "undefined") {
                         if (payload.code === "AlexaShopping.RetryLaterError") {
                             speechText =  "Looks like there was an issue. Let's get back to the skill.";
                         } else {
                             speechText = "I'm sorry, shopping indicated an issue while performing your request. Please try again later.";
                             console.info(`[INFO] Shopping Action had an issue while performing the request. ${payload.message}`);
                        }
                    } else if (token === "AddToListToken") {
                        console.info(`[INFO] Shopping Action: Add to list action was a success for ${token}.`);
                        speechText = "Thank you for adding the product to your wish list.";
                    }
            break;
            default :
                console.info(`[INFO] Shopping Action: There was a problem performing the shopping action.`);
                speechText = "There was a problem adding the item to your wish list.";
            }
        }

        return handlerInput.responseBuilder.speak(speechText).getResponse();
    },
};

Implement the purchase flow

The AMAZON.BuyShoppingProducts action helps the customer purchase a recommended product. When you invoke the action, Alexa takes the customer through a multi-turn interaction that presents the product and final price, and asks for confirmation to purchase the product. The purchasing workflow incorporates various checks, including valid payment method, delivery address, and voice PIN authorization associated with the customer account.

To purchase products within an Alexa skill, the customer must have a default 1-click setting enabled on their Amazon account. If the customer doesn't have this option enabled, Alexa Shopping informs the customer to enable the required feature, and then returns the result to your skill in a response with status code InvalidCustomerSettings.

Complete the following steps to implement the Alexa Shopping AMAZON.BuyShoppingProducts action in your skill code.

Send a directive to buy a product

After your skill receives permission from the customer to shop for the product, you send the Connections.StartConnection directive to Alexa with a AMAZON.BuyShoppingProducts payload.

The follow example shows a request to start the product purchase flow.

Copied to clipboard.

/**
 * Handler to handle RecommendAndAddToList.
 */
const RecommendAndAddToListHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'RecommendAndAddToList');
    },
    handle(handlerInput) {
        console.log("RecommendAndAddToList received.");

        const speechText = "<Product intro>" +
        " Would you like to know how to purchase this on Amazon?"

        return handlerInput.responseBuilder
            .speak(speechText)
            .reprompt(speechText)
            .getResponse();
        }
};

/**
 * Handler to handle YesAndNoIntentHandler.
 */
const YesAndNoIntentHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'IntentRequest'
            && (request.intent.name === 'AMAZON.YesIntent'
                || request.intent.name === 'AMAZON.NoIntent');
    },
    handle(handlerInput) {
        const intentName = handlerInput.requestEnvelope.request.intent.name;
        console.log("User said : " + intentName);

        if (intentName === 'AMAZON.YesIntent') {
            var actionText = "Staging item with Amazon"
            let actionTask = {
              'type': 'Connections.StartConnection',
              'uri': 'connection://AMAZON.BuyShoppingProducts/1',
              'input': {
                   'products' : [
                     {
                       'asin' : 'ASN1'
                     }
                   ]
                },
              'token': 'PurchaseProductToken'
          };

            return handlerInput.responseBuilder
            .speak(actionText)
            .addDirective(actionTask)
            .getResponse();
        }

        const speechText = "All right. Please come back if " +
                              "you need more product recommendations."

        return handlerInput.responseBuilder
            .speak(speechText)
            .getResponse();
        }
};

Handle a session resumed request

Your skill receives the result of a purchase as a SessionResumedRequest. Implement a handler to receive the response.

The response includes the token given in the AMAZON.BuyShoppingProducts request. Use the token value to restore the previous session of the skill before you invoked the action. Resume the skill based on the result included in the payload.

If the action fails, Alexa informs the customer of the error and provides available recovery mechanisms to the customer. Alexa Shopping Actions returns the error to your skill in the payload of the response. Don't notify the customer about the error, but use the result to alter the flow of skill execution.

The following example shows how to handle a session resumed request response.

Copied to clipboard.

/**
 * Handler to handle SessionResumedRequest.
 */
const SessionResumedRequestHandler = {
    canHandle(handlerInput) {
        const request = handlerInput.requestEnvelope.request;
        return request.type === 'SessionResumedRequest';
    },
    handle(handlerInput) {

        // Session attributes and ID are same as previous IntentRequest
        const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();
        console.log("sessionAttributes : " + JSON.stringify(sessionAttributes));
        const token = handlerInput.requestEnvelope.request.cause.token;

        let request = handlerInput.requestEnvelope.request;
        let speechText = "Sorry, I had trouble doing what you asked. Please try again.";

         if (request.cause) {
             const token = request.cause.token;
             const status = request.cause.status;
             const code = status.code;
             const message = status.message;
             const payload = request.cause.result;

             console.info(`[Shopping Response] ${JSON.stringify(request)}`);

             console.info(`[INFO] Shopping Action Result: Code - ${code}, Message - ${message}, Payload - ${payload}`);

             switch(code) {
                 case '200':
                     if (typeof payload !== "undefined") {
                         if (payload.code === "AlexaShopping.RetryLaterError") {
                             speechText =  "Looks like there was an issue. Let's get back to the skill.";
                         } else {
                             speechText = "I'm sorry, shopping indicated an issue while performing your request. Please try again later.";
                             console.info(`[INFO] Shopping Action had an issue while performing the request. ${payload.message}`);
                        }
                     } else if (token === "PurchaseProductToken") {
                             console.info(`[INFO] Shopping Action: Buy action was a success for ${token}.`);
                             speechText = "Thank you for purchasing this product.";
                         }
                    break;
            default :
                console.info(`[INFO] Shopping Action: There was a problem performing the shopping action.`);
                speechText = "There was a problem purchasing this product. Let's get back to the skill.";
            }
        }

        return handlerInput.responseBuilder
            .speak(speechText)
            .getResponse();
    },
};

Handle a refund request

To handle refund requests, direct the customer to review the order in their Amazon account. No Alexa Shopping Actions task exists to process a refund.

Copied to clipboard.

 const RefundHandler = {
     canHandle(handlerInput) {
         return util.parseIntent(handlerInput) === 'RefundIntent';
     },
     handle(handlerInput) {

         return handlerInput.responseBuilder
         .speak("Please go to the orders section of your Amazon account to review or cancel your order.")
         .getResponse();
     },
 }

Implement the recommend-products flow

When your skill invokes the AMAZON.RecommendShoppingProducts action, Alexa presents details of the recommended products. Then, the customer can add one or more items to their Cart, buy the items, or continue with the skill. Customers can add items to the Cart even if they don't define customer settings, such as payment method and delivery address.

Complete the following steps to implement the Alexa Shopping AMAZON.RecommendShoppingProducts action in your skill code.

Send a directive to recommend products

After you ask the customer for permission to learn more about recommended products, you can send the Connections.StartConnection directive to Alexa with the AMAZON.RecommendShoppingProducts payload. Here, you request a response on error only.

The following example shows a request to add the specified product to the list.

Copied to clipboard.

/**
* Handler to handle RecommendProducts.
*/
const RecommendProductsHandler = {
     canHandle(handlerInput) {
         const request = handlerInput.requestEnvelope.request;
         return request.type === 'IntentRequest'
             && (request.intent.name === 'RecommendProducts');
     },
     handle(handlerInput) {
         console.log("RecommendProducts received.");

         const speechText = "<Product intro>" +
         " Would you like hear more?"

         return handlerInput.responseBuilder
             .speak(speechText)
             .reprompt(speechText)
             .getResponse();
         }
};

/**
* Handler to handle YesAndNoIntentHandler.
*/
const YesAndNoIntentHandler = {
     canHandle(handlerInput) {
         const request = handlerInput.requestEnvelope.request;
         return request.type === 'IntentRequest'
             && (request.intent.name === 'AMAZON.YesIntent'
                 || request.intent.name === 'AMAZON.NoIntent');
     },
     handle(handlerInput) {
         const intentName = handlerInput.requestEnvelope.request.intent.name;
         console.log("User said : " + intentName);

         if (intentName === 'AMAZON.YesIntent') {
             var actionText = "Transferring product recommendations to Alexa"
             let actionTask = {
               'type': 'Connections.StartConnection',
               'uri': 'connection://AMAZON.AddToList/1',
               'input': {
                    'products' : [
                      {
                        'asin' : 'ASN1'
                    },
                    {
                      'asin' : 'ASN2'
                    }
                  ]
                 },
               'token': 'RecommendProducts',
               'onCompletion': 'SEND_ERRORS_ONLY'
           };

             return handlerInput.responseBuilder
             .speak(actionText)
             .addDirective(actionTask)
             .withShouldEndSession(true)
             .getResponse();
         }

         const speechText = "All right. Please come back if you need more product recommendations."

         return handlerInput.responseBuilder
             .speak(speechText)
             .getResponse();
         }
};

Handle a session error

Alexa sends a response on skill connection failure only. Your skill receives the response as a System.ExceptionEncountered response. For details, see System.ExceptionEncountered. If you want to know if an error occurred, implement a handler to receive the response.

The following example shows how to receive a the System.ExceptionEncountered response.

Copied to clipboard.

/**
* Handler to handle ExceptionEncountered.
*/
const SystemExceptionEncounteredHandler = {
   canHandle(handlerInput) {
      const request = handlerInput.requestEnvelope.request;
      return request.type === 'System.ExceptionEncountered';
   },
   handle(handlerInput) {
      console.dir(handlerInput.requestEnvelope.request);
      return handlerInput.responseBuilder
         .getResponse();
   },
};