Skip to main content

Forwarding Voicemail Transcriptions to Email - Python


This guide will show how you can easily take a voicemail message from incoming callers, transcribe the recording, and email the transcription. We will use the SignalWire Python SDK to record a voicemail and transcribe it along with the MailGun API to send an email.

What do I need to run this code?

As we said before, there are a few packages and resources you will want to have ready to go before running this script. You can find out more information about them here! Once you've got these on hand, you're ready to go!

There are two ways you could run this script easily. The first way is through using our pre-built docker image for the script or by building your own. You could also run it through Flask! Here are some more resources around those:

As with all guides and snippets, you will need your API credentials (SignalWire Space URL, API Token, and Project ID). These can be found under the API tab of your SignalWire Space. For more information around that, check here.

Code Breakdown

Setting Up Your Environment File

  1. Copy from 'example.env' and fill in your values
  2. Save the new file as '.env'

Your file should look something like this, where SIGNALWIRE_SPACE is the full name of your SignalWire Space. e.g.:, SIGNALWIRE_PROJECT is your SignalWire Project ID, and SIGNALWIRE_TOKEN is your SignalWire API token.

EMAIL_SUBJECT=Transcribed Voicemail

The Code Itself

Our first route is the entry point for all incoming calls. In this route, we will use Say to play a quick prompt and Record to take a message. We will redirect to the /hangup route when the recording is finished as specified by the action URL. We will also make use of recording status callbacks and transcription status callbacks so that SignalWire will send an http request to our specified routes when the recordings and transcriptions are complete.

# Entry point for incoming calls, prompts to leave a voice mail for processing.
@app.route('/voice_entry', methods=['GET', 'POST'])
def voice_entry():
# instantiate voice response
response = VoiceResponse()

# play voicemail prompt and record voicemail that will finish on # key
response.say("Please leave a message at the beep. Press the pound key when finished.")
response.record(finishOnKey="#", maxLength="1", action="/hangup", transcribe="true", transcribeCallback="/transcribe_webhook", recordingStatusCallback="/recording_webhook")
# return response
return str(response)

Our next route /hangup is the action URL that will execute when the recording is complete. We will play a short message to say goodbye and then use Hangup to end the call.

# Terminates the call
@app.route('/hangup', methods=['GET','POST'])
def hangup():
# instantiate voice response
response = VoiceResponse()
# say goodbye and hangup
response.say('Thank you! Good Bye!')
# return response
return str(response)

Now we will define a function send_email(body) that will utilize the MailGun API to send an email using the variables from our .env file.

# Sends the email provided using mailgun
def send_email(body):
"" + os.environ['MAILGUN_DOMAIN'] + "/messages",
auth=("api", os.environ['MAILGUN_API_TOKEN']),
data={"from": os.environ['EMAIL_FROM'],
"to": [os.environ['EMAIL_TO']],
"subject": os.environ['EMAIL_SUBJECT'],
"text": body })

This route /recording_webhook will be called when the recording is completed. We will use it for information purposes only - for the sake of printing to console all the necessary information about recordings. Read more about recording status callbacks in our status callbacks super guide.

# The webhook that is called on recording updates
@app.route('/recording_webhook', methods=['GET','POST'])
def recording_webhook():
# For information only, and to show what is returned.

return "200"

Our last route/transcribe_webhook will be used to handle the transcription status callback when the transcription is complete. We will use the TranscriptionText parameter as the body in our send_email(body) function.

# The webhook that is called when a transcription is received, this will process the transcription actions.
@app.route('/transcribe_webhook', methods=['GET','POST'])
def transcribe_webhook():
# get all request values

# Recording Information
recording_duration = request.values.get("RecordingDuration")
recording_url = request.values.get("RecordingUrl")

# Send Email
return "200"


After all is said and done, you'll start receiving voicemail transcriptions of any incoming voicemail that has gone to whatever phone number(s) you've decided to run it on. And as we said, there are two ways you could run this.

Build and Run on Docker

  1. Use our pre-built image from Docker Hub
docker pull signalwire/snippets-voicemail-transcription:latest

(or build your own image)

  1. Build your image
docker build -t snippets-voicemail-transcription .
  1. Run your image
docker run --publish 5000:5000 --env-file .env snippets-voicemail-transcription
  1. The application will run on port 5000

Build and Run Natively

To run the application, execute export then run flask run.

You may need to use an SSH tunnel for testing this code if running on your local machine. – we recommend ngrok. You can learn more about how to use ngrok here.

Sign Up Here

If you would like to test this example out, you can create a SignalWire account and space here.

Please feel free to reach out to us on our Community Slack or create a Support ticket if you need guidance!