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.
Custom gadgets that provide smart home functionality will not pass certification.
- Overview
- How custom events work
- Prerequisites
- Directives for custom events
- Custom event types
- Custom events and session attributes
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.

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 theLaunchRequest
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.
{
"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"
}
}
]
}
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;
}
};
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;
}
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 |
---|---|---|---|
|
The directive type. Must be set to |
|
yes |
|
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 |
|
yes |
|
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 |
|
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. |
int64 |
yes |
|
Free-form JSON object that the skill will receive if (and only if) the event handler duration expires. |
Object |
no |
|
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 |
|
The JSON object that represents the JSON logic against which the events are evaluated. If this expression is satisfied, the corresponding For examples, see Filter expression examples. |
Object (jsonlogic) |
yes |
|
The action to be performed on a successful filter expression match. Valid values are |
enum { |
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.
{
"shouldEndSession": false,
"outputSpeech": {
"type": "SSML",
"ssml": "<speak>Game over!</speak>"
},
"directives": [
{
"type": "CustomInterfaceController.StopEventHandler",
"token": "1234abcd-40bb-11e9-9527-6b98b093d166"
}
]
}
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;
}
};
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();
}
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 |
---|---|---|---|
|
The directive type. Must be set to |
|
yes |
|
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 |
|
yes |
Custom event types
After you start an event handler using StartEventHandler
, your skill can receive two types of requests:
CustomInterfaceController.EventsReceived
– Received when an event meets the event handler's filter conditions.CustomInterfaceController.Expired
– Received when the event handler's duration expires.
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.
{
"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"
}
}
]
}
}
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;
}
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();
}
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 |
---|---|---|
|
The event type, which is |
|
|
The ID of the request that Alexa made to the skill. |
|
|
The time of the request that Alexa made to the skill. |
|
|
The unique |
|
|
A list of events that meet the filter criteria. |
Array |
|
The object that contains the header of the event. |
Object |
|
The gadget-defined namespace (interface name) of the event. |
|
|
The gadget-defined name of the event. |
|
|
An object that contains the endpoint ID of the gadgets that sent the event. |
Object |
|
The endpoint ID of the gadget that sent the event. |
|
|
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.
{
"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"
}
}
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;
}
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();
}
}
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 |
---|---|---|
|
The type of event, which is |
|
|
The ID of the request from Alexa. |
|
|
The time of the request from Alexa. |
|
|
Free-form JSON object that the skill will receive if (and only if) the event handler duration expires. This is specified by |
Object |
|
The unique |
|
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:
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:
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.
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
.
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.
Last updated: Feb 14, 2022