Receive a Custom Event from a Gadget


To enable a custom skill to process and act on information from your gadget, you can have your gadget send custom events to the skill. The custom events include an arbitrary payload that is unique to your gadget.

To support custom events, you first define a Custom Interface and, in your gadget's firmware, include code that can encode the custom events that you defined. Then, your skill code can receive these custom events by using the Custom Interface Controller. This is an interface that you add to your skill when you set it up using the Alexa Skills Kit developer console or the Alexa Skills Kit Command-Line Interface (ASK CLI).

This topic describes how to set up your skill to receive custom events. In this topic, we refer to custom events as simply events. For instructions on how to define your interface and what you need to do on your gadget to support it, see Learn About Custom Interfaces. You can also send custom directives from your skill to your gadget, as described in Send a Gadget a Custom Directive from a Skill.

Overview

Because gadgets do not connect directly to the Alexa cloud, they use an Echo device to handle communication with the skill. The following figure shows how a gadget sends events to a skill through an Echo device.

Sending a custom event from an Alexa gadget to a skill.

How custom events work

Your skill does not receive events from a gadget automatically. Your skill must indicate to Alexa that it wants to receive events, the type of events it wants to receive, the criteria that the events must meet, and how long Alexa should forward events to the skill. To do so, the skill sends Alexa a directive that configures an event handler with this information.

Note that a gadget event cannot trigger a skill to launch. The skill must already be running and have sent a directive to start an event handler. This indicates that it expects to receive events from a gadget.

Each skill instance can have only one active event handler at a time. When you start a new event handler, it replaces the previous event handler. As your skill runs, it typically starts and stops event handlers many times.

Prerequisites

Before you add skill code to receive custom events from your gadget, do the following:

  • Ensure that your Echo device has at least the minimum software version listed in Before you get started. To find the software version of your Echo device, go to the Alexa app under Settings > Device Settings > [device] > About > Device Software Version. You can make sure that your Echo device has the latest software version by doing the following:
    • For Echo devices without a screen – Say "Alexa, update your software."
    • For Echo devices with a screen – On the Echo device, go to Settings > Device Options > Check for Software Updates.
  • Ensure that you defined your interface and that your gadget declares support for it. These aspects are described in Learn About Custom Interfaces.
  • Create a custom skill and add the Custom Interface Controller interface to the skill's supported interfaces, as described in Set up a Skill for a Gadget.

Directives for custom events

There are two directives that relate to event handlers:

  • CustomInterfaceController.StartEventHandler – Sends Alexa a command to configure and start an event handler to pass events to your skill. If you want your skill to be able to receive events as soon as possible, we recommend that you send this directive in response to the LaunchRequest from Alexa.
  • CustomInterfaceController.StopEventHandler – Sends Alexa a command to stop the current event handler, and therefore stop sending events to your skill. This is optional. If you do not send this directive, the event handler will automatically stop when the duration it specifies elapses.

StartEventHandler directive

This directive configures and starts an event handler, which enables the skill to receive custom events. A skill can have only one active event handler at a time.

The following is an example of a StartEventHandler directive, and how to assemble it using an SDK. Field descriptions follow the example.

Copied to clipboard.

{
    "outputSpeech": {
        "type": "SSML",
        "ssml": "<speak>Let's start the game!</speak>"
    },
    "shouldEndSession": false,
    "directives": [
        {
            "type": "CustomInterfaceController.StartEventHandler",
            "token": "1234abcd-40bb-11e9-9527-6b98b093d166",
            "expiration": {
                "durationInMilliseconds": 8000,
                "expirationPayload": {
                    "gameOverSpeech": "Game over! Would you like to hear your stats?"
                }
            },
            "eventFilter": {
                "filterExpression":{
                    "and": [
                        {"==": [{"var": "header.namespace"}, "Custom.Robot"]},
                        { "==": [{ "var": "endpoint.endpointId" }, "amzn1.ask.endpoint.ABCDEFGHIJKLMNOPQRSTUVWXYZ"]}
                    ]
                },
                "filterMatchAction": "SEND_AND_TERMINATE"  
            }
        }
    ]
}

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Node.js (v2).

const StartInputIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'StartEventHandlerIntent';
  },
  handle(handlerInput) {

      // Build a response that includes a directive that configures
      // the event handler. The directive shown here configures
      // the event handler to listen for events for 8 seconds and
      // receive only events that belong to the Custom.Robot namespace
      // and originate from a particular gadget. This configuration
      // also specifies that when an event that meets these criteria
      // is received, the event handler should stop.
      const response = handlerInput.responseBuilder
          .speak("Let's start the game!")
          .withShouldEndSession(false)
          .addDirective({
            'type': 'CustomInterfaceController.StartEventHandler',
            'token': '1234abcd-40bb-11e9-9527-6b98b093d166',
            'expiration': {
                'durationInMilliseconds': 8000,
                'expirationPayload': {
                  'gameOverSpeech': 'Game over! Would you like to hear your stats?'
                }
            },
            'eventFilter': {
                'filterExpression':{
                    'and': [
                        {'==': [{'var': 'header.namespace'}, 'Custom.Robot']},
                        {'==': [{'var': 'endpoint.endpointId' }, 'amzn1.ask.endpoint.ABCDEFGHIJKLMNOPQRSTUVWXYZ']}
                    ]
                },
                'filterMatchAction': 'SEND_AND_TERMINATE'
            }
         })
          .getResponse();

         // Write the response to CloudWatch.
         console.log("===RESPONSE=== "+ JSON.stringify(response));

         // Return the response.
         return response;

   }
};

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Java.

@Override
public Optional<Response> handle(HandlerInput input) {

    final String speechText = "Let's start the game!";

    // Use the request ID as the token, cache this value in the session attributes.
    String token = input.getRequest().getRequestId();
    input.getAttributesManager().getSessionAttributes().put(StatusGaugeHandler.TOKEN_KEY, token);

    // Build the StartEventHandler directive, which will enable the
    // skill to receive custom events. Note that a skill can only
    // have one active event handler at a time.
    StartEventHandlerDirective directive = StartEventHandlerDirective.builder()
        .withToken(token)
        .withEventFilter(EventFilter.builder()
                .withFilterExpression(JsonUtil.fromString(FILTER))
                .withFilterMatchAction(FilterMatchAction.SEND).build())
        .withExpiration(Expiration.builder()
            .withDurationInMilliseconds(30000l)
            .withExpirationPayload(JsonUtil.fromString(EXPIRATION_PAYLOAD)).build())
        .build();

    Optional<Response> response = input.getResponseBuilder()
            .withSpeech(speechText)
            .withSimpleCard(RobotEventHandler.SKILL_TITLE, speechText)
            .addDirective(directive).build();

    System.out.println("=== Returning response === " + JsonUtil.asString(response.get()));
    return response;
}

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Python.

def build_start_event_handler_directive(token, duration=30000):
    """Builds the custom directive to start the event handler.

    :param token: the unique identifier to be associated with the event handler
    :param duration: the active duration of the event handler in milliseconds
    :return: the custom directive to start the event handler
    """

    filter_expression = {"==": [{"var": "header.namespace"}, "Custom.Event"]}

    payload = {"data": "Your skill session time expired. Bye!"}
    return StartEventHandlerDirective(
        token=token,
        event_filter=EventFilter(
            filter_expression=filter_expression,
            filter_match_action=FilterMatchAction.SEND
        ),
        expiration=Expiration(
            duration_in_milliseconds=duration,
            expiration_payload=payload))

The StartEventHandler directive contains the following fields.

Field Description Type Required

type

The directive type. Must be set to CustomInterfaceController.StartEventHandler.

string

yes

token

A unique identifier to associate with all custom events dispatched by an event handler. You should store this token to use as a filter to reject any events that show up after you've started a new event handler. You can use the requestId of the request that Alexa made to the skill.

string that represents UUID16

yes

expiration

An object that defines the duration of the event handler and the optional JSON payload that the skill will receive if (and only if) the event handler duration expires.

Object

yes

expiration.durationInMilliseconds

The length of time, in milliseconds, for which events from connected gadgets will be passed to the skill. Your skill will continue to receive events until this duration expires or the event handler is otherwise stopped.
Minimum: 1,000 (1 second).
Maximum: 90000 (90 seconds).

int64

yes

expiration.expirationPayload

Free-form JSON object that the skill will receive if (and only if) the event handler duration expires.
Maximum size: 8,000 characters.

Object

no

eventFilter

Defines the jsonlogic event filter expression and its corresponding match action. This filter is applied to all events during the event handler's duration. Events that are rejected by the filter expression are not sent to the skill.

Object

no

filterExpression

The JSON object that represents the JSON logic against which the events are evaluated. If this expression is satisfied, the corresponding match action is performed.

For examples, see Filter expression examples.

Object (jsonlogic)

yes

filterMatchAction

The action to be performed on a successful filter expression match. Valid values are SEND and SEND_AND_TERMINATE. A SEND action sends all custom events that match the filter expression to the skill. A SEND_AND_TERMINATE action sends the custom event that matches the filter expression to the skill and then terminates the event handler. Termination using this approach does not trigger the expiration payload.

enum {SEND, SEND_AND_TERMINATE}

yes

Filter expression examples

StopEventHandler directive

This directive stops an event handler, if an event handler associated with the provided token is running. If the skill sends this directive before an event handler's duration expires, the skill will not be sent a CustomInterfaceController.Expired event. The following is an example of a StopEventHandler directive.

Copied to clipboard.

{
    "shouldEndSession": false,
    "outputSpeech": {
        "type": "SSML",
        "ssml": "<speak>Game over!</speak>"
    },
    "directives": [
        {
            "type": "CustomInterfaceController.StopEventHandler",
            "token": "1234abcd-40bb-11e9-9527-6b98b093d166"
        }
    ]
}

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Node.js (v2).

const StopInputIntentHandler = {
  canHandle(handlerInput) {
    return handlerInput.requestEnvelope.request.type === 'IntentRequest'
      && handlerInput.requestEnvelope.request.intent.name === 'StopEventHandlerIntent';
  },
   handle(handlerInput) {

      // Retrieve the session attributes, which is where
      // we stored the token that we specified when we
      // started the event handler.
     const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();

     // Start building a response.
     const responseBuilder = handlerInput.responseBuilder;
     responseBuilder.withShouldEndSession(false)
                    .speak("Game over!");

     // If we were able to retrieve an event handler token from the
     // session attributes, stop the event handler with it. If the
     // token was from an event handler that is no longer running,
     // it is ok because in that case, this directive will have no
     // effect.
     if (sessionAttributes.currentInputHandlerId) {
           // Add the directive.
           responseBuilder.addDirective({
               'type': 'CustomInterfaceController.StopEventHandler',
               'token': sessionAttributes.currentEventHandlerToken
             });
     }

      // Build the response.
      const response = responseBuilder.getResponse();

      // Write the response to CloudWatch.
      console.log('===RESPONSE=== '+ JSON.stringify(response));

      // Return the response.
      return response;

   }
};

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Java.

@Override
public Optional<Response> handle(HandlerInput input) {

    String speechText = "All right. See you later!";

    // Stops the event Handler with the matching token ID
    Map<String, Object> attributes = input.getAttributesManager().getSessionAttributes();
    String cachedToken = (String) attributes.getOrDefault(TOKEN_KEY, "MissingToken");

    StopEventHandlerDirective directive =
            StopEventHandlerDirective.builder().withToken(cachedToken).build();

    return input.getResponseBuilder()
            .withSpeech(speechText)
            .withSimpleCard(StatusGaugeHandler.SKILL_TITLE, speechText)
            .addDirective(directive).build();
}

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Python.

@sb.request_handler(can_handle_func=lambda handler_input:
                    is_intent_name("AMAZON.CancelIntent")(handler_input) or
                    is_intent_name("AMAZON.StopIntent")(handler_input))
def stop_and_cancel_intent_handler(handler_input):
    logger.info("stop_and_cancel_intent_handler: handling request")
    session_attr = handler_input.attributes_manager.session_attributes
    response_builder = handler_input.response_builder

    if EVENT_HANDLER_TOKEN_KEY in session_attr and session_attr[EVENT_HANDLER_TOKEN_KEY] is not None:
        token = session_attr[EVENT_HANDLER_TOKEN_KEY]
        response_builder.add_directive(StopEventHandlerDirective(token))
    return response_builder.speak("Alright, see you later.").response

The StopEventHandler directive contains the following fields.

Field Description Type Required

type

The directive type. Must be set to CustomInterfaceController.StopEventHandler.

string

yes

token

The unique identifier that is associated with the event handler that you want to stop. This token must match the token that you specified when you started the event handler using CustomInterfaceController.StartEventHandler.

string

yes

Custom event types

After you start an event handler using StartEventHandler, your skill can receive two types of requests:

Note that your skill might not receive events in the order in which they were generated. To keep track of the event order, you can generate a sequence ID and include that in your event payloads. On the skill side, you can inspect the sequence ID to determine if events were received in the correct order. However, other than detecting out-of-order events, your skill has very limited possibilities in handling out-of-order events, especially if you also want to use session attributes.

EventsReceived request

Your skill receives this type of request when an event meets the filter conditions that you provided in the StartEventHandler directive. The following is an example of an EventsReceived request. Field descriptions follow the example.

Copied to clipboard.

{
  "version": "1.0",
  "session": {
     "application": {},
     "user": {}
    }, 
    "request": {
        "type": "CustomInterfaceController.EventsReceived",
        "requestId":"amzn1.echo-api.request.406fbc75-8bf8-4077-a73d-519f53d172a4",
        "timestamp":"2018-12-02T01:29:40.027Z",
        "token": "1234abcd-40bb-11e9-9527-6b98b093d166",
        "events": [
            {
               "header" : {
                   "namespace":"Custom.Robot",
                   "name":"EyeBlink"
               },
               "endpoint": {
                   "endpointId": "amzn1.ask.endpoint.ABCD"
               },
               "payload":{
                   "ack": "ok"
               }    
            }
        ]
    }
}

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Node.js (v2).

    canHandle(handlerInput) {
      let { request } = handlerInput.requestEnvelope;
      console.log("CustomEventHandler: checking if it can handle " + request.type);
      return (request.type !== 'CustomInterfaceController.EventsReceived');
    },
    handle(handlerInput) {
      // Handles status acknowledgement from the robot.
      console.log("== Received custom event ==");

      let { request } = handlerInput.requestEnvelope;
      let payload = request.events[0].payload;

      if (payload.ack === "ok") {
        return response.speak("The robot's status was updated successfully")
          .getResponse();
      }
      return response;
    }

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Java.

@Override
public Optional<Response> handle(HandlerInput input) {

    EventsReceivedRequest event = (EventsReceivedRequest) input.getRequestEnvelope().getRequest();

    Map<String, Object> attributes = input.getAttributesManager().getSessionAttributes();
    String cachedEndpointId = (String) attributes.getOrDefault(ENDPOINT_KEY, "MissingEndpoint");
    String cachedToken = (String) attributes.getOrDefault(TOKEN_KEY, "MissingToken");

    if (!cachedToken.equals(event.getToken())) {
        // Ignore event
        System.out.println("==SessionAttributes==" + JsonUtil.asString(attributes));
        System.out.println("Ignoring event with token: " + event.getToken());
        return Optional.empty();
    }

    // Assuming only one event
    Event customEvent = event.getEvents().get(0);
    Header header = customEvent.getHeader();
    String endpointId = customEvent.getEndpoint().getEndpointId();
    String speechText = "Unexpected event received.";

    // Namespace is already checked by filter from start event handler
    if (EVENT_STATUS_ACK.equals(header.getName())) {

        // Verify that the event comes from the same gadget
        if (cachedEndpointId.equals(endpointId)) {
            JsonNode payload = JsonUtil.fromMap(customEvent.getPayload());
            if ("ok".equals(payload.get("ack").asText())) {
                speechText = "Successfully updated the robot's status";
            }
        }
    }

    return input.getResponseBuilder().withSpeech(speechText, PlayBehavior.ENQUEUE).build();
}

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Python.

@sb.request_handler(can_handle_func=is_request_type("CustomInterfaceController.EventsReceived"))
def gadget_event_handler(handler_input: HandlerInput):
    """Acknowledge if the message was successfully received by the gadget.

    :param handler_input: the data provided to all request handlers
    :return: a response indicating if the message was successfully received
    """
    logger.info("gadget_event_handler: handling event from gadget")

    custom_event = handler_input.request_envelope.request  # type: EventsReceivedRequest
    session_attr = handler_input.attributes_manager.session_attributes
    response_builder = handler_input.response_builder

    # Validate event token
    cached_token = session_attr[EVENT_HANDLER_TOKEN_KEY]
    event_token = custom_event.token
    if cached_token != event_token:
        logger.info("Event token doesn't match. Ignoring this event")
        return response_builder.response

    # Validate event name (namespace was already checked by event handler filter)
    event = custom_event.events[0]
    if EVENT_NAME == event.header.name:
        payload = event.payload

        # Check the status message that the robot sent in the event
        if payload['ack'] == 'ok':
            return response_builder\
                .speak("The robot's status was updated successfully", PlayBehavior.ENQUEUE)\
                .response

    return response_builder\
        .speak("I am unable process this event", PlayBehavior.ENQUEUE)\
        .response

The EventsReceived request contains the following fields.

Field Description Type

type

The event type, which is CustomInterfaceController.EventsReceived.

string

requestId

The ID of the request that Alexa made to the skill.

string

timestamp

The time of the request that Alexa made to the skill.

string

token

The unique token, specified by StartEventHandler, of the event handler that dispatched this event.

string

events

A list of events that meet the filter criteria.

Array

events.header

The object that contains the header of the event.

Object

events.header.namespace

The gadget-defined namespace (interface name) of the event.

string

events.header.name

The gadget-defined name of the event.

string

events.endpoint

An object that contains the endpoint ID of the gadgets that sent the event.

Object

events.endpoint.endpointId

The endpoint ID of the gadget that sent the event.

string

events.payload

The free-form JSON object that contains the gadget-defined fields of the event of this namespace and type.

Object

Expired request

Your skill receives this type of request when the event handler expires. The following is an example of an Expired request. Field descriptions follow the example.

Copied to clipboard.

{  
   "version": "1.0",
   "session": {
      "application": {},
      "user": {}
   }, 
   "request": {
      "type": "CustomInterfaceController.Expired",
      "requestId":"amzn1.echo-api.request.406fbc75-8bf8-4077-a73d-519f53d172a4",
      "timestamp":"2018-12-02T01:29:40.027Z",
      "expirationPayload": {
            <JSON Object>
      },
      "token": "1234abcd-40bb-11e9-9527-6b98b093d166"
    }
}

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Node.js (v2).

    canHandle(handlerInput) {
      let { request } = handlerInput.requestEnvelope;
      console.log("CustomEventHandler: checking if it can handle " + request.type);
      return request.type === 'CustomInterfaceController.Expired';
    },
    handle(handlerInput) {
      console.log("== Custom event expiration input ==");

      let { request } = handlerInput.requestEnvelope;
      let data = request.expirationPayload.data;
      let response = handlerInput.responseBuilder
        .withShouldEndSession(true)
        .speak(data)
        .getResponse();
      response.directives = response.directives || [];
      return response;
    }

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Java.

public class ExpirationEventHandler implements RequestHandler {

    @Override
    public boolean canHandle(HandlerInput input) {
        return input.matches(requestType(ExpiredRequest.class));
    }

    @Override
    public Optional<Response> handle(HandlerInput input) {

        ExpiredRequest event = (ExpiredRequest) input.getRequestEnvelope().getRequest();
        JsonNode payload = JsonUtil.fromMap(event.getExpirationPayload());
        String speechText = payload.get("data").toString();

        return input.getResponseBuilder()
                .withSpeech(speechText, PlayBehavior.ENQUEUE)
                .withShouldEndSession(true)
                .build();
    }
}

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Python.

@sb.request_handler(can_handle_func=is_request_type("CustomInterfaceController.Expired"))
def event_handler_expiration_handler(handler_input):
    """Stops the Event Handler and end the skill session.

    :param handler_input: the data provided to all request handlers
    :return: a response indicating if the skill has ended
    """
    logger.info("event_handler_expiration_handler: handling expiration event")

    request = handler_input.request_envelope.request  # type: ExpiredRequest
    response_builder = handler_input.response_builder
    if request.expiration_payload is None:
        speech_text = "Expiration payload is missing"
    else:
        logger.info("Expiration payload: " + str(request.expiration_payload))
        speech_text = request.expiration_payload['data']

    return response_builder.set_should_end_session(True).speak(speech_text).response

The Expired request contains the following fields.

Field Description Type

type

The type of event, which is CustomInterfaceController.Expired.

string

requestId

The ID of the request from Alexa.

string

timestamp

The time of the request from Alexa.

string

expirationPayload

Free-form JSON object that the skill will receive if (and only if) the event handler duration expires. This is specified by StartEventHandler in the expiration.expirationPayload field.

Object

token

The unique token, specified by StartEventHandler, of the event handler that dispatched this event.

string

Handling events

This section contains examples of ways that your skill might examine an incoming event.

Validate the endpoint ID

If you expect that your skill will receive a custom event from a gadget in response to a custom directive, the skill should validate that the event came from the expected gadget. You can do this by validating the endpoint ID.

First, however, you must store the list of connected gadgets (endpoint IDs) that you found using the Endpoint Enumeration API as follows:

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Node.js (v2).

let { context } = handlerInput.requestEnvelope;
let { apiEndpoint, apiAccessToken } = context.System;
let response;
try {
  console.log("checking endpoint");
  response = await Endpoint.getConnectedEndpoints(apiEndpoint, apiAccessToken);
  console.log("v1/endpoints response: " + JSON.stringify(response));

  if ((response.endpoints || []).length === 0) {
    console.log('No connected endpoints available');
    response = handlerInput.responseBuilder
    .speak("No endpoints found. Please try again after connecting your gadget.")
    .getResponse();
    return response;
  }

  // Store the endpoint IDs so that later, you can check whether
  // custom directives and events are from the same gadget.
  let endpointId = response.endpoints[0].endpointId;
  console.log("Received endpoints. Storing Endpoint Id: " + endpointId);
  const attributesManager = handlerInput.attributesManager;
  let sessionAttributes = attributesManager.getSessionAttributes();
  sessionAttributes.endpointId = endpointId;
  attributesManager.setSessionAttributes(sessionAttributes);
}

After storing the endpoint IDs of the connected gadgets, you can validate the endpoint ID when you receive a custom event, as follows:

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Node.js (v2).

const CustomEventHandler = {
   canHandle(handlerInput) {
      return handlerInput.requestEnvelope.request.type === 'CustomInterfaceController.EventsReceived';
   },

   handle(handlerInput) {

      const request = handlerInput.requestEnvelope.request;

      // Retrieve the session attributes, which is where we stored the
      // endpoint ID of the gadget that the skill expects to get events from.
      const sessionAttributes = handlerInput.attributesManager.getSessionAttributes();

      // Validate the endpoint ID.
      // For simplicity, we assume that there is only one event.
      let endpointOfCurrentRequest = request.events[0].endpoint.endpointId;
      return endpointOfCurrentRequest == sessionAttributes.endpointId;
   }
};

Validate the event namespace and name

When your skill receives an event, you will likely want to check the namespace and name of the event before processing it further. The namespace and name of the received event will correspond to the namespace and name of an event that you defined in your Custom Interface. The following is an example of how to implement this validation.

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Node.js (v2).

const EXPECTED_NAMESPACE = "Custom.Robot";
const EXPECTED_NAME = "Blink";

const CustomEventHandler = {
   canHandle(handlerInput) {
      return handlerInput.requestEnvelope.request.type === 'CustomInterfaceController.EventsReceived';
   },

   handle(handlerInput) {

      const request = handlerInput.requestEnvelope.request;
      var infoToSpeak = '';

      // Find the event namespace and name.
      // For simplicity, we assume that there is only one event.
      let namespace = request.events[0].header.namespace;
      let name = request.events[0].header.name;
      console.log('Validating event from namespace ' + namespace + ' and name ' + name);

      // Validate the namespace and name against what you expect.
      if (namespace != EXPECTED_NAMESPACE || name != EXPECTED_NAME) {
         infoToSpeak = 'I am unable to process this event.';
      }
      else {
         infoToSpeak = 'There was an event from namespace ' + namespace + ' and name ' + name;
      }

      return handlerInput.responseBuilder
        .speak(infoToSpeak)
        .withShouldEndSession(false)
        .getResponse();
      }
   }
};

Handle the expiration payload

When you configured the event handler using StartEventHandler, you might have specified a plain-text expiration.expirationPayload for Alexa to speak when the event handler expires (that is, when the skill receives an Expired event).

The following example shows how you can have Alexa speak text that you stored in a gameOverSpeech field that you defined within expiration.expirationPayload.

Copied to clipboard.

This code example uses the Alexa Skills Kit SDK for Node.js (v2).

const EXPECTED_NAMESPACE = "Custom.Robot";
const EXPECTED_NAME = "Blink";

const ExpiredEventHandler = {
   canHandle(handlerInput) {
      return handlerInput.requestEnvelope.request.type === 'CustomInterfaceController.Expired';
   },

   handle(handlerInput) {

      const request = handlerInput.requestEnvelope.request;
      let response = handlerInput.responseBuilder
        .speak(request.expirationPayload.gameOverSpeech)
        .getResponse();
      response.directives = response.directives || [];
      return response;

   }
};

Custom events and session attributes

When your gadget sends multiple events quickly, it is possible for your skill to be invoked concurrently. If you are using session attributes to save data during the skill session, keep in mind that updates to session attributes are not guaranteed to be atomic. Simultaneous updates to session attributes can result in a newly added attribute being overridden by the response from another concurrent request. For example:

Events sent sequentially to skill:

--> Request for Event1 has sessionAttributes = {}
<-- Response to Event1 has sessionAttributes = {'sawevent1': true}
--> Request for Event2 has sessionAttributes = {'sawevent1': true}
<-- Response to Event2 has sessionAttributes = {'sawevent1': true, 'sawevent2': true}
--> Request for Event3 has sessionAttributes = {'sawevent1': true, 'sawevent2': true}

Events sent concurrently to skill:

--> Request for Event1 has sessionAttributes = {}
--> Request for Event2 has sessionAttributes = {}
<-- Response to Event1 has sessionAttributes = {'sawevent1': true}
<-- Response to Event2 has sessionAttributes = {'sawevent2': true}
--> Request for Event3 has sessionAttributes = {'sawevent2': true}

If you expect that your gadget might send multiple concurrent events to your skill, we recommend using a database such as DynamoDB (rather than session attributes) to store your skill's state.


Was this page helpful?

Last updated: Feb 14, 2022