No Fax Machine? No Problem! SparkPost, Twilio, and Cloudinary Are Here To Save The Day!
I don’t know about you, but I don’t have a fax machine. Once in a blue moon, I have to send or receive a fax, so I was excited when Twilio shared an awesome way to receive faxes straight to your email using SparkPost. But that is only half the battle. I decided to build the ability to send faxes from an email so I would never need a fax machine again!
We’ll build a function so we can send a PDF attachment to FAX_NUMBER@YOUR_DOMAIN, which will automatically fax the PDF to the phone number before the at sign (a.k.a. the local part, for all you email geeks out there). To do this, we’ll use SparkPost’s inbound functionality, Twilio’s Fax API, and Cloudinary to glue them together. We’ll receive an email to a Twilio function, pull off the attached PDF, save it to Cloudinary, and send it as a fax.
Sign Up and Configure Your SparkPost Account
The first thing you’ll need is a SparkPost account and a domain you want to use to receive mail (a.k.a. an inbound domain). You’ll also need to create an API key with permissions to read and writeinbound domains
and relay webhooks
Next, add the SparkPost MX records for your inbound domain. Once you verify that they are set up correctly, run the following cURL to add your inbound domain.
curl -XPOST \
https://api.sparkpost.com/api/v1/inbound-domains \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "domain": "YOUR_DOMAIN" }'
Sign Up For Cloudinary
Next, we’ll need a Cloudinary account to store the PDF we’ll fax. If you don’t know, Cloudinary is a powerful solution for storing, manipulating, and delivering all your media. Grab your cloud name, API key, and API secret and put them somewhere safe for later.
Create & Configure Twilio
We’ll use Twilio to consume the incoming emails and send the faxes. To get started, sign up for an account, and buy a phone number that can send and receive faxes.
Twilio’s serverless functions are perfect for this project. This isn’t a stateful application and it doesn’t need to be running all the time. Using one, we can quickly get set up and run our application.
We’ll need the following NPM modules for our function:
Let’s also add in our environment variables for Cloudinary and enable Twilio’s option to pass through our ACCOUNT_SID
and AUTH_TOKEN
so we can use their client library without worrying about our credentials:
The Code
First things first, let’s write our basic Twilio function and pull in our dependencies:
const cloudinary = require('cloudinary')
const { MailParser } = require('mailparser')
const { toArray } = require('lodash')
exports.handler = function(context, messages, callback) {
const twilio = context.getTwilioClient()
cloudinary.config({
cloud_name: context.CLOUDINARY_NAME,
api_key: context.CLOUDINARY_KEY,
api_secret: context.CLOUDINARY_SECRET
})
}
The messages
variable is an object full of the messages SparkPost handed off to us. We’ll need to loop through the messages, pull off the attachment, save it to Cloudinary, and send off the fax.
// after the cloudinary config
const promises = toArray(messages).map((message) => {
const toNumber = pickPhoneNumber(message)
return pickPdfAttachment(message)
.then((attachment) => {
if (!attachment) { return false }
return uploadAttachment(attachment)
})
.then((mediaUrl) => {
return sendFax(twilio, toNumber, mediaUrl)
})
})
Promise.all(promises)
.then(() => callback(null, 'success'))
.catch((error) => callback(error))
Parsing The Email
You can see an example the payload SparkPost sends in the API documentation. The important parts for us are the rcpt_to
and the
content
which will contain the raw email. These live inside message.msys.relay_message
.
We can pull out the phone number by splitting off the local part from the rcpt_to
value – everything before the @
.
function pickPhoneNumber(message) {
return message.msys.relay_message.rcpt_to.split('@')[0]
}
We’ll also need to pull the PDF off of the message. To do this, we’ll parse the RFC 822 value using the mailparser
library and return the content of the first PDF attached.
function pickPdfAttachment(message) {
return new Promise((resolve, reject) => {
const content = message.msys.relay_message.content
const isBase64 = content.email_rfc822_is_base64
const body = isBase64 ? Buffer.from(content.email_rfc822, 'base64') : content.email_rfc822
const parser = new MailParser({ streamAttachments: true })
let attachment
parser.on('data', (data) => {
if(!attachment &&
data.type === 'attachment' &&
data.contentType === 'application/pdf') {
attachment = data.content
}
})
parser.on('error', reject)
parser.write(body)
parser.end(() => resolve(attachment))
})
}
Saving The Attachment
Assuming we get an attachment, we’ll need to put it in an accessible spot for Twilio to pull it from. Enter Cloudinary. Using their Node library we can easily pipe the PDF and get the publicly accessible URL.
function uploadAttachment(attachment) {
return new Promise((resolve, reject) => {
const stream = cloudinary.v2.uploader.upload_stream((error, result) => {
if (error) {
reject(error)
}
else {
resolve(result.url)
}
})
attachment.pipe(stream)
})
}
Sending The Fax
The last step for our code is to send the fax! Twilio makes this really easy. We need three values: the number we are sending to, the number we are sending from, and the media url. We’ll use the number we bought earlier to send for the “from” number. And we should have a phone number from the local part of the email and the media url from Cloudinary!
function sendFax(twilio, toNumber, mediaUrl) {
return twilio.fax.v1.faxes.create({
to: toNumber,
from: YOUR_PHONE_NUMBER,
mediaUrl: mediaUrl,
})
.then((res) => {
console.log('sent', mediaUrl, 'to', toNumber)
})
}
Create A Relay Webhook
The last piece of the puzzle it to tie our inbound domain from SparkPost to our Twilio function. Let’s create a relay webhook to pass all mail sent to our inbound domain onto our Twilio function. Copy your function path and pass it into this cURL request to create the relay webhook. Make sure that “Check for valid Twilio signature” is unchecked so that SparkPost can access it!
curl -XPOST \
https://api.sparkpost.com/api/v1/relay-webhooks \
-H "Authorization: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "target": "YOUR_TWILIO_PATH", "match": { "domain": "YOUR_INBOUND_DOMAIN", "protocol": "SMTP" }, "name": "Email-to-Fax" }'
Send The Fax!! 🎉
Send an email over to FAX_NUMBER@YOUR_DOMAIN with your fax attached as a PDF and it should go through!
You can set up receiving faxes for a fully working email fax machine with some guidance from our friend Patrick at Twilio! Feel free to reach out if you’ve got any questions and have fun faxing!
-Avi
P.S. Before you launch this to the world, you’ll probably want to validate the phone numbers you’re sending to and add some security to who can send. I’d suggest a token in the email that you verify in the Twilio function.
The post How To Send Faxes Via Email Using SparkPost, Twilio & Cloudinary appeared first on SparkPost.
Top comments (0)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.