How to redirect users to a different intent in your Alexa skill

Or why you might want to return a default response based on user criteria.

Tom Berwick
Alexa Skills Dev
3 min readJul 15, 2022

--

Photo by Brandon Romanchuk on Unsplash

As I’ve written in a previous article, requestInterceptors allow you to do some pre-processing work on all incoming requests. The problem with requestInterceptors however is you can’t short-circuit the response loop and return and arbitrary response based on some criteria e.g. you want to limit the number of games a user can play unless they’re a a subscriber. You could in theory have a function that you call on every intent. But this could get repetitive and you could accidentally miss an intent out. However for arguments sake it might look similar to below:

const canPlay = (handlerInput) => {
const {attributesManager} = handlerInput;
const sessionAttributes = attributesManager.getSessionAttributes();

const {isPremium = false} = sessionAttributes;
// any other logic you might have to determine if a user can play such as after a specific time duration
return isPremium
}
const someIntent = {
canHandle(handlerInput){
return //criteria for this intent
},
handle(handlerInput){
if(!canPlay) {
return handlerInput.responseBuilder.speak("sorry you can't play at the moment").getResponse()
} }}

As you can see you would just need to add this check in for every intent where applicable, you could refactor out the response as well to keep it in one place e.g a function that returns responseBuilder.speak("sorry you can't play at the moment") . That way you can keep all that logic in one place like so.

return sorryYouCantPlay(handlerInput)

There is an alternative approach however. If you take a look at the someIntent above (or if you use the NodeJS sdk in general) you will notice that each intentHandler we define has a handle method. This method is used to determine whether or not our handler should be invoked for this particular request and you’re probably used to seeing examples like the following:

canHandle(handlerInput){
return handlerInput.requestEnvelope.request.type === "LaunchRequest";
}
//OR
canHandle(handlerInput) {
return (handlerInput.requestEnvelope.request.type === "IntentRequest" && handlerInput.requestEnvelope.request.intent.name === "AMAZON.YesIntent");
}

So how can we use this to our advantage? Well we can create a whole new intentHandler that returns true or false when certain conditions are met. So you might have something along the lines of the following ( canPlay would be the same function as we defined above):

const CanPlayRequestIntentHandler = {canHandle(handlerInput) {const {
attributesManager,requestEnvelope: {request: { type },}} = handlerInput;
return ( (type === "LaunchRequest" || type === "IntentRequest") &&!canPlay(handlerInput)); },handle(handlerInput) {const {responseBuilder} = handlerInput; return responseBuilder.speak("Sorry you've already played today, come back tomorrow to try again").speak()}

Then all that’s left todo is to plug this intent handler into our list of valid intent handlers, but the trick here is to put it first, otherwise the response will be executed from the first intent handler it can find that will fulfil the request.

exports.handler = Alexa.SkillBuilders.custom().addRequestHandlers(CanPlayRequestIntentHandler,LaunchRequestHandler
///more handlers).lambda()

As you can see there are two ways to achieve this kind of behaviour, but which do you prefer? I personally like the second approach as it keeps all the behaviour in one intentHandler rather than having an if statement in all your intents — you might wonder why we didn’t just do this in the launchIntentHandler but someone could potentially launch your skill via a specific intent and not just the launch one. But the first approach might be more immediately obvious if someone else is picking up your code.

Let me know your thoughts in the comments. If this helped you please give it a clap and follow the publication for more articles

Update

You’ll notice here I’ve used sessionAttributes to store the criteria I want to check in. whilst you might want to persist a value in sessionAttributes there might be occasions where you still want to test for something, but don’t want to pollute the sessionAttributes with these values. For these you could use a requestInterceptor and add items to your requestAttributes . You can grab this via the attributesManager.getRequestAttributes() method and setRequestAttributes() accordingly. These attributes only last for the lifecycle of a request so won’t hang around.

--

--

Tom Berwick
Alexa Skills Dev

Mobile App, game and full stack developer. Constantly trying to learn new things and dabble in growth hacking