
This tutorial is part of the Quick & Dirty series for developing a custom Alexa skill called My Zoo.
Estimated time: 10 – 15 minutes
No programming experience necessary
Overview
With this tutorial, you’ll learn to use Amazon S3 storage to store and retrieve persistent data.
What is persistent data?
It’s nice when Alexa remembers stuff about a user’s previous interaction with the skill. Persistent data helps Alexa remember that stuff. So, let’s update My Zoo to remind a returning user what animal they heard last time they visited the zoo!
A couple notes …
Amazon says using DynamoDB is better for storing persistent data. I wrote this tutorial because several of Amazon’s more detailed tutorials use S3. Consider skipping this tutorial and go directly to Add Persistent Data to a Custom Alexa Skill using Amazon DynamoDB.
* Alexa Development Console (ADC)-hosted skills, like My Zoo, along with its S3 data storage, is limited in its free-ness. If you publish a skill and it becomes popular, you may need to move the skill to your own AWS account. But we’re not launching My Zoo for public use, so don’t worry about that right now!
Prerequisites
- an Amazon Developer account, click here to create one (it’s free)
The instructions in this post are applicable to any custom Alexa skill coded with Python. If you wish to join-in on the My Zoo fun, complete these 3 mini-tutorials first (total estimated time: 25 – 40 minutes):
Ready? Let’s do it!
Use Persistent Data with Amazon S3
Step 1: Add Code Requirements
First, we need to do programmer-techie stuff with code requirements. If you’re not a coder, no worries, just follow these steps:
- Log into the Alexa developer console and open My Zoo.
- Click Code on the top menu bar.
- Double-click the requirements.txt in the left column. The file will open in the editor window to the right.

- Add this line to the bottom of the file:
ask-sdk-s3-persistence-adapter
The requirements.txt file should now look like this:

- Click Save.
Step 2: Apply Code Requirements
- Double-click the lambda_function.py in the left column. Or, if the file is already open, click the lambda_function.py tab above the editor window.
- Find this line in the editor window:
import ask_sdk_core.utils as ask_utils
- Create a new line below it, and add this code:
import os from ask_sdk_s3.adapter import S3Adapter s3_adapter = S3Adapter(bucket_name=os.environ["S3_PERSISTENCE_BUCKET"])
- Find this line:
from ask_sdk_core.skill_builder import SkillBuilder
- Replace it with this line of code:
from ask_sdk_core.skill_builder import CustomSkillBuilder
Your updated section of code should now look like this:
import logging
import ask_sdk_core.utils as ask_utils
import os
from ask_sdk_s3.adapter import S3Adapter
s3_adapter = S3Adapter(bucket_name=os.environ["S3_PERSISTENCE_BUCKET"])
from ask_sdk_core.skill_builder import CustomSkillBuilder
from ask_sdk_core.dispatch_components import AbstractRequestHandler
from ask_sdk_core.dispatch_components import AbstractExceptionHandler
from ask_sdk_core.handler_input import HandlerInput
from ask_sdk_model import Response
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
- Scroll to the bottom of the file and find this line:
sb = SkillBuilder()
- Replace it with this line of code:
sb = CustomSkillBuilder(persistence_adapter=s3_adapter)
- Click Save.
Step 3: Save Data
Next, update the CaptureAnimal intent logic created in Create a Custom Alexa Skill, Part 3 (Code an Intent) to store the most recent animal/sound pair retrieved.
A reminder about Python: formatting matters. Make sure lines are indented as shown.
- Find the CaptureAnimal intent code block by searching for this line:
class CaptureAnimalIntentHandler(AbstractRequestHandler):
- Scroll down the code block and create a new line after this one:
speak_output = animal + 's say ' + sound
- Copy/paste this code:
animal_attributes = { 'animal': animal, 'sound': sound } attributes_manager = handler_input.attributes_manager attributes_manager.persistent_attributes = animal_attributes attributes_manager.save_persistent_attributes()
Make sure attributes_manager ...
lines up with animal_attributes ...
.
The entire CaptureAnimalIntentHandler should now look like this:
class CaptureAnimalIntentHandler(AbstractRequestHandler):
"""Handler for CaptureAnimal Intent."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_intent_name("CaptureAnimal")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
animal = handler_input.request_envelope.request.intent.slots["animal"].value
sounds = {
'bat': 'screech screech',
'bee': 'buzzzz buzzzz',
'cat': 'meow meow',
'chicken': 'cluck cluck',
'cow': 'mooooo',
'dog': 'bark bark',
'human': 'hello',
'lion': 'roar',
'monkey': 'chitter chatter',
'pig': 'oink oink',
'snake': 'hiss rattle hiss',
'zebra': 'whinny'
}
try:
sound = sounds[animal]
speak_output = animal + 's say ' + sound
animal_attributes = {
'animal': animal,
'sound': sound
}
attributes_manager = handler_input.attributes_manager
attributes_manager.persistent_attributes = animal_attributes
attributes_manager.save_persistent_attributes()
except:
speak_output = "I have not learned that animal's sound yet."
prompt = " Try another one!"
speak_output = speak_output + prompt
return (
handler_input.response_builder
.speak(speak_output)
.response
)
return (
handler_input.response_builder
.speak(speak_output)
.response
)
- Click Save.
Now, whenever a user asks for an animal and Alexa returns its sound, the last animal/sound pair will be saved to the S3 database.
Step 4: Retrieve Data
Add a returning user’s last animal/sound pair to their welcome message. If the user is new, stick with the standard “Welcome to My Zoo…” message.
- Find the LaunchRequest intent code by searching for this:
class LaunchRequestHandler(AbstractRequestHandler):
- Find this line:
speak_output = "Welcome to My Zoo, I make animal sounds! What animal do you want to hear?"
and replace it with this block of code:
# extract persistent attributes, if they exist attr = handler_input.attributes_manager.persistent_attributes attributes_exist = ('animal' in attr and 'sound' in attr) if attributes_exist: animal = attr['animal'] sound = attr['sound'] speak_output = "Welcome back to My Zoo! \ Last time you heard the {animal} {sound}. \ What animal do you want to hear now?" \ .format(animal=animal, sound=sound) else: speak_output = "Welcome to My Zoo, I make animal sounds! What animal do you want to hear?"
The entire LaunchRequestHandler should now look like this:
class LaunchRequestHandler(AbstractRequestHandler):
"""Handler for Skill Launch."""
def can_handle(self, handler_input):
# type: (HandlerInput) -> bool
return ask_utils.is_request_type("LaunchRequest")(handler_input)
def handle(self, handler_input):
# type: (HandlerInput) -> Response
# extract persistent attributes, if they exist
attr = handler_input.attributes_manager.persistent_attributes
attributes_exist = ('animal' in attr and 'sound' in attr)
if attributes_exist:
animal = attr['animal']
sound = attr['sound']
speak_output = "Welcome back to My Zoo! \
Last time you heard the {animal} {sound}. \
What animal do you want to hear now?"\
.format(animal=animal, sound=sound)
else:
speak_output = "Welcome to My Zoo, I make animal sounds! What animal do you want to hear?"
reprompt = "I do not know that one. What other animal do you want to hear?"
return (
handler_input.response_builder
.speak(speak_output)
.ask(reprompt)
.response
)
- Click Save.
- Click Deploy, wait for it to finish.
Step 5: Test
The first time you test after adding persistent data, Alexa won’t have any data to pull from, so you’ll be welcomed as a new user instead of a returning user.
- Click Test.
- Enter
my zoo
. If you already have a separate browser open for testing, remember to refresh it first. You should see/hear the normal “Welcome to My Zoo, …” - Enter an animal that exists in our little database, for example
lion
. - Enter
bye
to close out the session. - Enter
my zoo
again. Verify you’re getting the returning-user welcome-back message “Welcome back to My Zoo! Last time you heard …”.

Pretty cool, huh?!
Step 6: (optional): Delete Persistent Data
In order to test the “welcome” message for new users, you’ll need to delete persistent data (otherwise, you’ll always hear the “welcome back” message)
Delete persistent data directly from Amazon S3 … which may sound more intimidating than it actually is. Here goes:
- Click Code on the top menu bar.
- Between the top menu bar and the editor window, notice a line of icons.

- Find “S3 Storage” and click it.
- A new browser window will open to the Amazon Web Services (AWS) S3 management console. This is where the persistent data is written when you programmed to save the data in Step 3.
- Find the “breadcrumbs” listing near the top of the page.

- Click the long link name (your “breadcrumb” link name will look different that the example above). This will take you to the directory where persistent data is written.
- A new persistent data file is written for each user. Notice the file listing. You’ll probably only see 1 file with a name that starts with ‘amzn1.ask.account.’ unless you’ve been testing with different user accounts. This is your persistent data.

- Click the box next to the ‘amzn1.ask.account….’ file.
- Click the Delete button.
- You may see a warning “You don’t have permission…”. Ignore it.
- Scroll to the bottom and type
delete
in the confirmation field.

- Click Delete objects.
- You can safely close the AWS S3 browser window.
Now if you go back and test again, you should see the new user welcome.
Conclusion
Congratulations! You now know how to save and retrieve persistent data from an AWS S3 bucket. Knowing how to do this will open many options for extending the features of your custom Alexa skills.
As a next step, I suggest doing the Alexa Skill Sample Python First Skill – Module 3 tutorial. It covers the same concepts introduced in this post, but in more depth.
My next post (coming soon) will demonstrate using DynamoDB instead of S3 for saving persistent data.
I thank you for visiting my blog. If you have corrections or suggestions for this tutorial series, please add a comment below.
Related Resources
Alexa Skill Kit (ASK)
Tutorial: Build an Engaging Alexa Skill
Alexa Skill Sample Python First Skill – Module 3
Leave a Reply