When you think about an email address, you probably think of it as something that doesn’t change, such as your co-worker’s address, your friend’s personal address, or a company support address. You likely don’t think of an email address as something that spawns as needed: a generated email address.
Generated email addresses are a great way to make your SaaS product a lot more convenient to use. Consider the following two scenarios:
- An issue tracking tool lets users send emails directly to a project. These emails automatically create new issues in the project. Whenever someone updates an issue, the person assigned to it is emailed. They can then reply to that email and the issue is automatically updated without requiring that user to log in to the app.
- A job applicant tracking tool lets aspiring candidates send a resume and cover letter to a customized email address rather than through a web form. These emails create new application records in the tracking tool allowing a hiring team to review, respond and schedule interviews with the applicant.
Both scenarios require a way to map incoming emails to the right place. The only practical way to do this is if the email addresses are generated from within the app. For instance, every time a new project is created in the issue tracker, a custom email address is assigned to that project. Similarly, every time a new job opening is created in the job applicant tracker, a custom email address is assigned for application submissions. This way you can store the generated email addresses in a database, reference it whenever emails are sent in, and map the addresses accordingly.
This is exactly how we handle incoming emails with DoneDone, an issue tracking and customer support tool. For the past six years, we’ve relied on Postmark to process all of our incoming application emails. Users can send emails to a custom project address to log an issue or reply back to application emails to update issues.
In this post, I’ll show you how we use G Suite (formerly Google Apps) and Postmark to process generated emails. Before we begin, you should have a G Suite account tied to your product’s domain name, and you should have configured MX records for the domain to point to Gmail. While this tutorial highlights how we use this technique for issue tracking, generated email addresses could be used to update anything in your SaaS product.
Set up a catch-all address for incoming email #
The first step is to set up a new user in our G Suite account. This user’s email address will serve as the catch-all address for any random email addresses pointing to our domain. In our case, for example, we can set up a user with an email address of
Route emails to your catch-all address #
Next, we want Gmail to forward all inbound emails that do not already map to an existing G Suite user account to our catch-all address. The key to routing an email to the right place is in the local-part of the generated email address.
For example, a support desk project might have an address of
email@example.com. An email update to an issue might have a reply-to address that looks something like
firstname.lastname@example.org. When DoneDone receives email, it looks at the local-part of the address (e.g.
c8h29d73-issue-reply) and the from address of the email. With those two pieces of information, the application knows who the email is from and what it is in reference to.
In the Google Admin portal, go to Gmail’s routing settings. The admin portal is a bit of a jungle, so I just type “routing” in the search bar and select the result pointing to Gmail’s “Advanced settings.”
Once there, scroll down to the “Routing” section. Rollover the Routing subsection and click “Add” to add a new routing rule. At this point, a modal window should pop up with routing options.
Here’s what to fill out:
- Name the routing rule. I call mine “Catch-all routing.”
- Under Messages to affect, check “Inbound.”
- Under For the above types of messages, do the following
- Select “Modify message” from the dropdown.
- Under Headers, check “Add X-Gm-Original-To header.”
- Under Envelope Recipient, check “Change envelope recipient.”
- Next, select the "______@ existing-domain" option. In the input field, type in the local-part of the email address for the user you just created. (e.g. “catch-all”).
- Next, select the “Show Options” link.
- Under Account types to affect, check ”Unrecognized / Catch-all.”
- Finally, save the new routing rule.
So, what did we accomplish here? We created a rule letting Gmail know to direct any emails to an unrecognized email address in our domain to the catch-all address. Now, whatever randomly generated email address we create will land there!
Configure inbound forwarding and processing in Postmark #
Now that we have our catch-all address setup, we need to:
- Forward emails from this inbox into our Postmark server.
- Set up an inbound webhook to deliver the email as JSON data to our product.
Click the above links for the instructions to complete the steps.
Parse the incoming Postmark webhook #
Finally, we’ve reached our moment of glory. With the webhook URL setup, we now need to build a webhook on our end to process the incoming emails from Postmark.
At first glance, we might simply look at the “To” value of the incoming JSON payload to pull the generated email address that the message was sent to:
In this case, we’ve got what we need to update the issue assigned to the
email@example.com email address.
However, there’s a big catch.
When creating new issues, some of our customers forward emails directly from their support address to the generated new issue address. For instance, instead of sending emails directly to
firstname.lastname@example.org, emails are forwarded from, say,
email@example.com. This way, users don’t have to remember a cryptic address when creating new issues.
Here’s the problem. When Postmark receives emails that are set up this way, the “To” address shows the original “To” email address instead of the address that we generated:
Fortunately, there’s a simple fix. Remember all the work we did to set up the catch-all routing? This is where it pays off.
Every email routed into Postmark from our catch-all address will contain a specific header named
X-Gm-Original-To. This is what we configured in Step 3b, two sections above. The value of this email header will always be the generated email address, regardless of whether emails are sent directly to the address or are forwarded from another address.
Since we can always rely on the
X-Gm-Original-To header, this is what we inspect to determine the true “To” address for all of our inbound emails. In our webhook code (written in C#), we can do something like this to pull the address from the deserialized Postmark JSON object:
var originalReplyToAddress = Headers.Single(h => h.Name == "X-Gm-Original-To").Value;
We can then pull the message body, attachments, and any other information out of the email to use as part of the issue creation or update.
I hope this tutorial helps you see the value in generated email addresses, and that in conjunction with Postmark and G Suite, you can use them to set up a system like the one we’ve set up at DoneDone for yourself.