SIP Voicemail
Having voicemail available is an important part of any phone system, and calls to SIP endpoints are no exception. There are a couple different ways to handle voicemails for SIP. You could use a Domain Application set up with two cXML application webhooks. The first would dial your SIP endpoint with a timeout parameter. The second would record a voicemail if the call was not answered within the timeout time.
To demonstrate how to set this up without a Domain application, this example will accomplish the same task with the Compatibility SDK with Python. We will configure a DID (SignalWire phone number) to accept incoming calls and dial our SIP endpoint, then record a voicemail if the call is unanswered. You can clone the GitHub repo to follow along, test, and change to meet your needs.
Setup Your Environment
First, you need to provide the application with environmental variables.
Copy the contents of env.example
and save them in a new file called .env
.
Fill in your SignalWire credentials.
# Project ID copied from the API Credentials in your SignalWire Space
PROJECT_ID=
# API token copied from the API Credentials in your SignalWire Space
API_TOKEN=
# Your SignalWire Space URL copied from the API Credentials in your SignalWire Space
SIGNALWIRE_URL=
If you need help finding this information, check out our guide to Navigating Your SignalWire Space.
The Compatibility SDK will pull these environmental variables from the .env
file without any import statement as long as the .env
file is in the same parent directory.
Run Your Server
This example serves our webhooks using a Flask
server. After your environmental variables are set, you can install dependencies with
pipenv install
then start the Flask server with pipenv run dev
.
If you prefer to use Docker, build the image with docker build -t sipvoicemail .
and run it with docker run --p 3000:3000 --env-file .env sipvoicemail
.
Test Endpoints with Ngrok
Now, we need a way for our SignalWire service to reach those webhooks. SignalWire requires
that your webhooks be publicly accessible for them to be used with our services. So, we
recommend using Ngrok to provide an HTTPS URL for testing.
In your Ngrok CLI, run ngrok http 3000
, where 3000 is the port we set in our Flask server.
It will return a secure URL you can copy for the next step.
Configure a Number to Accept Incoming Calls
For this voicemail setup, a phone number is the intermediary that accepts incoming calls and dials your SIP endpoint. In your SignalWire Dashboard, you can purchase a phone number and edit its settings to direct calls to the Ngrok URL. The settings for your phone number of choice will look something like this:
With your server and Ngrok running, you should now be able to dial this number and test this example.
Code Walkthrough
All the logic of our program lives within the index.py file. It is a simple Flask server serving just three POST routes: an entry route, a voicemail route, and a hangup route.
When a call comes in, our setup will hit the default endpoint we have defined.
It says a message then dials our SIP endpoint. It is important to note that our
dial
method has a 15 second timeout before its "action" redirects it to the
voicemail route.
The voicemail route will confirm that the call status is "completed" then ask the caller to record a message. It then starts a recording, and its "action" directs the call the the hangup route when the recording finishes.
The hangup route receives the recording object then hangs up the call.
You will notice that these routes all use the same syntax to use the Compatibility SDK. We generate XML to run and send it with the following lines.
response = VoiceResponse()
# logic with XML verbs, for example:
response.say("Welcome to SignalWire.")
return Response(str(response), mimetype="text/xml")
Following the same pattern, you can use cXML to extend this example to suit your needs.
Wrap Up
This guide shows you one of the ways to set up voicemail if your phone system ends at SIP endpoints.
For an example of a full IVR using the Python Compatibility SDK, see Dynamic IVR using Python. Although that example dials numbers, you can easily substitute SIP endpoints as we did here.