Incoming email hooks and you!

Monday, November 11, 2019

Sometimes when you are making your cool app thing you want to accept email. This could be for a myriad of reasons.

Consider the Github use case. When someone comments on an issue you are watching you will receive an email. If you reply to the email it adds a comment to the issue!

Let’s learn about some cool email headers and how we might go about building that ourselves.

I’ll be using Postmark as the email sender/receiver. Most other email services like them also have this functionality. I just think they are cool and use them for my own projects.

Setup

I’m going to skip a bit past the initial steps of creating an account and a server. Postmark has that well documented.

What we care about is the incoming email hook.

First we go to default inbound stream.

Alt Text

Then in the settings tab the inbound email hook

Alt Text

We’ll also want to set an inbound email domain. You’ll have to look up How to set that up in your DNS

Alt Text

Okay so we have our quick setup. Postmark has great docs so check those out for more setup info. Though to note it’s very important to have a custom inbound domain to accomplish what we want to do!

Sending our email

Let’s send our email to the user. What do we want to track how we will know it is them?

Message-ID

Message-ID
<42.0B.41170.86A79BB5@twitter.com>
Message-ID
<42.0B.41170.86A79BB5@twitter.com>
Message-ID
<42.0B.41170.86A79BB5@twitter.com>
Message-ID
<42.0B.41170.86A79BB5@twitter.com>
Message-ID
<42.0B.41170.86A79BB5@twitter.com>
Message-ID
<42.0B.41170.86A79BB5@twitter.com>

170.86A79BB5@twitter.com>`

In-Reply-To

This email header is the starting point for how email clients thread emails. When you reply to an email your client puts the Message-ID of the email sent to you in the In-Reply-To header

References

This header contains the Message-ID s of all the emails in the email thread. Every time a new email is send in a thread the Message-ID of the email being replied to is also appened to the References field

Reply-To

When you send an email you can set a reply to address that differs from the to address you sent the email from. These headers are going to enable our workflow.

So let’s send our email.

// Require:
const postmark = require("postmark");

// Send an email:
const client = new postmark.Client("POSTMARK-SERVER-API-TOKEN-HERE");
const emailId = await generateAndStoreNewEmailId(commentThread, user);
client.sendEmail({
  "From": "sender@example.com",
  "To": "recipient@example.com",
  "Reply-To": `
inbound+${emailId}@inbound.ourdomain.com
`
  "Subject": "Test",
  "TextBody": "Hello from Postmark!",
  "Headers": [{Name: "Message-ID", Value: `
<${emailId}@inbound.ourdomain.com>
`}]
});

Okay so notice we set our Reply-To to an inbound email with an id we generated. We also want to store the user we are sending this email to and what comment thread.

Now we wait for a reply

Handling the inbound email

When any email is sent to our inbound email domain we will get a post request to our webhook. The entire message is quite big you can see it here. We just care about a few fields. We will use them to lookup the context for this email and add the comment.

The reason we used the inbound+${emailId} in our Reply-To was because postmark is nice enough to parse that id off for us and add it to a field called MailboxHash .

Now we can lookup the thread this email is talking about from the emailId.

const emailInfo = await getEmailInfo(inboundEmail.MailboxHash)

Postmark is also nice enough to parse the email body and give us just the reply text. We will use that for the commend body!

const body = inboundEmail.TextBody;

Then we can do something.

await createCommentInThread(
    emailInfo.commentThreadId,
    emailInfo.user,
    body
)

Conclusion

We also stored the email id in the Message-ID so if for some reason we can’t find our comment thread via Reply-To we can search the References or In-Reply-To fields to check as well.

This is the general strategy that GitLab uses

This was a pretty high level overview but hopefully it helps you understand how easy it can be to make your app accept incoming emails!

This post is also available on DEV.