Build A Fax Portal with Twilio, Python and Flask
Time to read: 6 minutes
It's nearly 2020 and can you believe that faxes are still alive?
Without fail, it seems that when the weather turns nasty, it's inevitable that we'll be forced to drive to the local business center and pay too much per sheet for what should just be an email.
Follow along as we utilize Twilio's fax enabled numbers and a simple web portal to send our own faxes, quickly and cost effectively, and from the comfort of your residence!
Project Dependencies
We'll be using a Twilio Programmable Fax number, Python 3, Flask, and the Twilio Python Helper library as our core requirements. Optionally, we'll add in Twilio SMS to receive the status of our submissions.
Twilio
We're not going anywhere if we don't have a Twilio Fax-Enabled number, so if you do not already have one, head on over and sign up for a free account.
Once you have registered, buy a phone number in the console. Be sure to select the Fax capability!
Search for a number that meets your region criteria then press buy.
Python
To keep our libraries separate from the system libraries, we follow Python best practices and create a virtualenv and activate it:
Our project has numerous dependencies to help make our life easier. Installation of the dependencies is as simple as:
Ngrok
Already excited for the prospect of running our code, we jump ahead of ourselves a bit by downloading and installing ngrok by following their instructions. Ngrok will help us expose our web service to the internet, which will be required for Twilio to retrieve our PDF from us!
A Flask Skeleton
Begin by creating a file called app.py
. At the top, we'll list out all of the import statements we'll be using:
Next, we begin creating our application and configuring it:
The SECRET_KEY
variable is used by Flask to create secure user sessions. In a production deployment, it is a good idea to set the environment variable of the same name to a random string of characters, but during development you can leave that variable undefined and a new random key will be generated each time you run the application.
The UPLOAD_FOLDER
variable needs to be a valid path on the file system and accessible by the user running this program. ALLOWED_EXTENSIONS
is a set which defines what file types we'll allow to be uploaded (currently Twilio only allows PDFs so don't change it). The Twilio account SID and Token are imported from environment variables, which you will set later when running the application. Next, we have the number from which our fax will appear to have been sent (this can be any of your verified numbers), and the mobile number to which the transmission status will be sent via SMS, both also imported from the environment.
Upload Form and Route
With configuration out of the way, it's time to move onto the core of this project: presenting a UI to upload the PDF and actually sending that file to Twilio! Our web form is relatively simple as all we are interested in is the PDF to be uploaded, and to what number should it be sent to:
As Twilio requires numbers to be specified in E.164 format, we take care to validate the number as best we can by utilizing the Twilio Lookup API.
Now it's time to create a route in Flask that can display the form with a GET request and handle the submission via a POST:
We begin by securely saving the uploaded file onto the file system, being sure to use the secure_filename
helper function to prevent attacks. Next we prepare the arguments including the from and to numbers, the URL for the PDF file and the number to send the status SMS. Finally, we submit all this information to Twilio, and redirect back to the form, in case another fax needs to be sent.
PDF Download Route
You might notice that we do not submit the PDF directly, instead, we provide a URL. This is because Twilio will soon turn around and attempt to fetch the file from that URL. So in order to handle that request, we create the following route:
Status Callback Route
Now that Twilio has the file, they're taking care of transmitting it to the recipient. You might notice the status_callback
keyword argument that we specified when creating the fax. This allows Twilio to inform us via a POST request if it was delivered successfully or not. So let's add that final route to the application:
Since we know that Twilio is finished with our file, we first make sure to delete it from the file system. Now since we don't like being kept in the dark, we fire off an SMS to tell us (hopefully) that our fax was successfully sent!
HTML Template
Our routes and logic are all well and good, but we do need to display some HTML. We can do this by creating the templates/fax.html
template file:
Running the Application
In our existing terminal window with our activated virtualenv, it is time to run the flask application. Define all of your environment variables we referenced in the configuration section above and drop them in the appropriate places here, then start the application:
If you are following this tutorial on Windows, then use set
instead of export
to define your environment variables in the command prompt. Assuming all went well, you will see the flask server doing its thing on port 5000:
Now because it's only listening on the localhost and because you're most likely behind a firewall or NAT of some type, it is the time to launch the ngrok utility that we downloaded earlier.
In another terminal window, navigate to where you downloaded and extracted ngrok, then run:
This exposes an http tunnel to the internet, meaning that traffic on port 80 is forwarded to port 5000 on your local machine, which just so happens to be where your flask server is listening!
Take note of the Forwarding URL that ngrok displays - this is where you need to go to access your upload forms. Copy and paste that first Forwarding URL (it will be http://<something>.ngrok.io) into your web browser. You should be greeted with your upload form!
Populate the To field with a fax number, then select your PDF file, and finally hit send.
Don't worry about how you enter the phone number, as the care we took in validating the number will ensure that Twilio receives an E.164 formatted number regardless of how you enter it.
Congratulations, you've done it. You have bested 1970s technology and sent a fax over the internet! Sit back, relax, and wait for the SMS to arrive indicating a successful transmission.
Disclaimers / Best Practices
Neither Flask's run
command nor our usage of ngrok should be considered production ready. In lieu of the flask server, consider using a combination of uWSGI and Nginx. Additionally, the route that we created to send the files from the uploaded directory can be entirely removed and replaced with a location configured within Nginx to serve the static content directly, which is going to be more efficient.
The exposure of our files to the internet is also a risk. Consider uploading them to an object store such as AWS S3 and then generating pre-signed URLs that have a short expiration time for Twilio to use.
Configure your firewall rules to expose your Nginx server on port 80 and 443, setup a domain name and a free SSL certificate from Let’s Encrypt for an easy to remember and secure URL.
Finally, you may want to setup systemd service unit files to make sure that all of the components (uWSGI, nginx) are enabled at boot and are always running.
Or if you find like I do, that you need to fax once every blue moon, just fire up this application locally as shown in the previous section when you need it and stop it as soon as it completes!
Conclusion
Hopefully you've seen a portion of the power that Twilio, their Python Helper Library, and tools like Flask can provide.
All code can be found on github.
Thanks for your time, and I look forward to seeing what you can build with Twilio!
Scott Sturdivant
GitHub
Related Posts
Related Resources
Twilio Docs
From APIs to SDKs to sample apps
API reference documentation, SDKs, helper libraries, quickstarts, and tutorials for your language and platform.
Resource Center
The latest ebooks, industry reports, and webinars
Learn from customer engagement experts to improve your own communication.
Ahoy
Twilio's developer community hub
Best practices, code samples, and inspiration to build communications and digital engagement experiences.