Your system must support OAuth 2.0 and the authorization code grant type.
Understand Account Linking for Alexa Skills
Amazon Cognito - Post Confirmation Lambda Trigger
Calling DynamoDB using AWS Cognito triggers
AWS SDK for JavaScript CognitoIdentityProvider
Validate and Use Access Tokens in Custom Skill Code
For a request that requires authentication, your code should do at least two checks:
- Verify that the accessToken exists.
- Verify that the token represents a valid user in your resource server.
exports.handler = async (event, context) => {
let date = new Date();
const tableName = process.env.TableName;
const region = process.env.Region;
console.log(`Table: ${tableName} Region: ${region}`);
AWS.config.update({ region: "REGION" });
// If the required parameters are present, proceed
if (event.request.userAttributes.sub) {
let ddbParams = {
TableName: tableName,
Item: {
id: { S: event.request.userAttributes.sub },
UserEmail: { S: event.request.userAttributes.email },
given_name: { S: event.request.userAttributes.given_name },
family_name: { S: event.request.userAttributes.family_name },
address: { S: event.request.userAttributes.address },
phone_number: { S: event.request.userAttributes.address },
createdAt: { S: date.toISOString() },
},
};
// Call DynamoDB
try {
await ddb.putItem(ddbParams).promise();
console.log("Success");
} catch (err) {
console.log("Error", err);
}
console.log("Success: Everything executed correctly");
context.done(null, event);
} else {
// Nothing to do, the user's email ID is unknown
context.done(null, event);
}
};
Remind Customers of Important Tasks or Events with the Reminders API
Integrate the Reminders API with Your Skill to Deepen Customer Engagement
pre-defined set of schemas that you can leverage to set up your skill service to use proactive events. Your skill can send proactive events information that conforms to a schema via the Skill Management API (SMAPI) as part of a skill manifest. Each schema has a predefined template that represents the text that Alexa reads back to the customer.
Proactive Events API Reference
Proactive Events Sender (beta)
Get started with Amazon Alexa Skills Proactive Events API
Interface ProactiveSubscriptionChangedRequest
How to Add the ProactiveEvents API to Your Existing Alexa Skill
How to Send Media Event Notifications to Your Alexa Skill Customers with the ProactiveEvents API
import got from "got";
import {
AlexaClientId,
AlexaClientSecret,
AlexaUserId,
proactiveEventsApi,
} from "./config";
function getMediaContentAvailableSchema(userId: string, message: string) {
let timestamp = new Date();
let expiryTime = new Date();
expiryTime.setMinutes(expiryTime.getMinutes() + 60);
let referenceId = `ReferenceIdPairing-${new Date().getTime()}`;
const mediaContentJson = {
timestamp: timestamp.toISOString(),
referenceId: referenceId,
expiryTime: expiryTime.toISOString(),
event: {
name: "AMAZON.MediaContent.Available",
payload: {
availability: {
startTime: timestamp,
provider: {
name: "localizedattribute:providerName",
},
method: "RELEASE",
},
content: {
name: "localizedattribute:contentName",
contentType: "GAME",
},
},
},
localizedAttributes: [
{
locale: "en-GB",
providerName: "Provider Name",
contentName: message,
},
],
relevantAudience: {
type: "Unicast",
payload: {
user: userId,
},
},
};
return mediaContentJson;
}
const getToken = async () => {
try {
const response = await got.post("https://api.amazon.com/auth/o2/token", {
json: true,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
body: {
grant_type: "client_credentials",
client_id: AlexaClientId,
client_secret: AlexaClientSecret,
scope: "alexa::proactive_events",
},
form: true,
});
// {
// access_token: 'Atc|XXXXXXXXXXXXXXXXXXXXXXXXX',
// scope: 'alexa::proactive_events',
// token_type: 'bearer',
// expires_in: 3600
// }
const tokenRequestId = response.headers["x-amzn-requestid"];
console.log(`Token requestId: ${tokenRequestId}`);
const accessToken = response.body.access_token;
console.log(`accessToken: ${accessToken}`);
return accessToken;
} catch (error) {
return error.response.body;
}
};
const sendEvent = async (token: string, userId: string, message: string) => {
try {
const response = await got.post(
proactiveEventsApi.development.northAmerica.url,
{
json: true,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: getMediaContentAvailableSchema(userId, message),
}
);
if ([200, 202].includes(response.statusCode)) {
console.log("successfully sent event");
console.log(`requestId: ${response.headers["x-amzn-requestid"]}`);
} else {
console.log(`Error https response: ${response.statusCode}`);
console.log(`requestId: ${response.headers["x-amzn-requestid"]}`);
if ([403].includes(response.statusCode)) {
console.log(`userId ${userId} may not have subscribed to this event.`);
}
}
} catch (error) {
return error.response.body;
}
};
async function notify(userId: string, message: string) {
const token = await getToken();
const status = await sendEvent(token, userId, message);
return status;
}
notify(AlexaUserId, "Your device has been paired").then((p) => console.log(p));
- the
skill.json
has the right event(s) listed as being published - the device locale matches one of the locales included in the message
- the customer id (userId) is correct - it normally will change each time you disable/re-enable the skill
Skill Events in Alexa Skills Alexa Skill Events
- AccountLinkedBody
- AccountLinkedRequest
- Permission
- PermissionAcceptedRequest
- PermissionBody
- PermissionChangedRequest
- ProactiveSubscriptionChangedBody
- ProactiveSubscriptionChangedRequest
- ProactiveSubscriptionEvent
- SkillDisabledRequest
Using the Alexa Settings API to Look Up the Device Time Zone
Obtain Customer Settings Information with the Alexa Settings API
The Skill Messaging API is used to send message requests to skills.
Get the user current time from your Alexa Skill
const getCurrentDate = async (handlerInput) => {
const serviceClientFactory = handlerInput.serviceClientFactory;
const deviceId = handlerInput.requestEnvelope.context.System.device.deviceId;
try {
const upsServiceClient = serviceClientFactory.getUpsServiceClient();
return (userTimeZone = await upsServiceClient.getSystemTimeZone(deviceId));
} catch (error) {
if (error.name !== "ServiceError") {
return handlerInput.responseBuilder
.speak("There was a problem connecting to the service.")
.getResponse();
}
console.log("error", error.message);
}
};
const skillBuilder = Alexa.SkillBuilders.custom();
exports.handler = skillBuilder
.addRequestHandlers(...)
.withApiClient(new Alexa.DefaultApiClient())
.withSkillId(...)
.lambda();
When you use
.standard()
Skill Builder instead of the.custom()
one, you _ don't need to_ include.withApiClient(new Alexa.DefaultApiClient())
. The Standard includes it by default. Ref
export const getUnixTimestampForTimeZone = (deviceTimeZone: string) => {
dayjs.extend(utc);
dayjs.extend(timezone);
return dayjs().tz(deviceTimeZone).valueOf();
};
List of tz database time zones
Enhance Your Customer Experience with Real-Time Location Services for Alexa Skills
Use Location Services for Alexa Skills
If
context.Geolocation.locationServices.access
is "ENABLED" andcontext.Geolocation.locationServices.status
is "RUNNING", then the device is sharing location.
For stationary devices like the Amazon Echo, Alexa can provide you the device's physical address with the Device Address API. Stationary devices do not provide geo-coordinate data through location services.
New Location Services Permissions Inconsistency
const LOCATION_SERVICES_PERMISSION = ["alexa::devices:all:geolocation:read"];
const [permission] = LOCATION_SERVICES_PERMISSION;
if (!isPermissionGranted(handlerInput, permission)) {
return responseBuilder
.speak(
"Location Services permission is required to use this feature. Please enable it in the Alexa app."
)
.withAskForPermissionsConsentCard(LOCATION_SERVICES_PERMISSION)
.getResponse();
}
/**
* @param {Object} handlerInput Alexa handlerInput object
* @param {String} permission Alexa Skill permission to check for
* @returns {Boolean} true if permission is granted. false if it is not
* @description checks to see if the desired Skill permission is granted
*/
function isPermissionGranted(handlerInput, permission) {
const {
requestEnvelope: {
context: {
System: { user },
},
},
} = handlerInput;
return (
user.permissions &&
user.permissions.scopes &&
user.permissions.scopes[permission] &&
user.permissions.scopes[permission].status === "GRANTED"
);
}
Location-aware Alexa Skills with HERE Location Services
New Permissions Card for Requesting Customer Consent
//determine if the device is capable of supporting location services
var isGeolocationSupported =
context.System.device.supportedInterfaces.Geolocation;
var lat = geo.coordinate.latitudeInDegrees;
var lng = geo.coordinate.longitudeInDegrees;
Developing Location-Aware Alexa Skills
AMAZON.YesIntent and AMAZON.NoIntent Now Compatible with ASK Dialog Management Features
Using Yes/No Intents with Dialog Management
Collect all user utterance provided to your skill on a daily basis but your skill must have at least 10 unique users per locale in a day, in order for data to be available for that locale for that day
AMAZON.FallbackIntent in the Alexa Skills Kit (ASK) built-in library helps you handle unexpected utterances, or when a customer says something that doesn’t map to any intents in your skill aka “out-of-domain requests.”
There is no way to capture the utterance that triggered the FallbackIntent.
Use the New Fallback Intent to Respond Gracefully to Unexpected Customer Requests
Code First Alexa Skill Development With VS Code and the ASK SDK Controls Framework
5 Common Error Messages for Custom Alexa Skills and How to Troubleshoot Them
Alexa: "There was a problem with the requested skill's response." - response JSON your skill sent back was malformed
Alexa: "Unable to reach the requested skill" - Alexa is unable to hit your endpoint
Lambda functions and custom skills
You do not need to verify that requests are coming from the Alexa service yourself. Access to execute your function is controlled by permissions within AWS instead.
It is recommended that you also enable skill ID verification for the function and provide the skill ID (also called the application ID) for your skill. This means your function can be invoked only if the skill ID in the Alexa Skills Kit request matches the skill ID configured in the trigger.When using skill ID verification, you do not need to include code to verify that the request is intended for your service. Requests from unverified skills do not invoke your function.
Progressive response is interstitial SSML content (including text-to-speech and short audio) that Alexa plays while waiting for your full skill response.
Extend a Built-in Intent with Sample Utterances
You can extend the standard built-in intents by providing additional sample utterances. This is useful for invoking the intents with skill-specific utterances.
You cannot extend any of the other built-in intents, such as WeatherForecast or LocalBusiness.
Available standard built-in intents for Alexa-enabled devices with a screen
AWS Certified Alexa Skill Builder – Specialty (AXS-C01) Exam Learning Path
Request and Response JSON Reference
Request Types Reference (LaunchRequest, CanFulfillIntentRequest, IntentRequest, SessionEndedRequest)