Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2008 (Washington, D.C.) with: Qasim Rasheed

Sending Mail And Monitoring Bounce Backs With ColdFusion And Postmark

By Ben Nadel on
Tags: ColdFusion

After I found out that you cannot use a GMail account to monitor bounce backs with the CFMail FailTo attribute, I wanted to take a look at Postmark. Postmark is an API-based service that helps web applications send email and track bounce backs. The service charges for email delivery - $1.50 for every 1,000 emails; but, you get 1,000 free credits when you sign up (no billing information required), so I figured it would be worth trying.

 
 
 
 
 
 
 
 
 
 

The Postmark API is simple and straightforward - they have one API for sending emails and one API for monitoring bounce backs. While I am not yet sure how to most effectively leverage the bounce back API methods, it took all of about 3 minutes to get some sample code up and running. The API is based on JSON (Javascript Object Notation) data packets that define your email parameters. Using CFHTTP, all you have to do it post your API key and your serialized JSON data structures.

In the following demo code, I'm gonna use CFHTTP to send an email to an invalid email address using Postmark.

  • <!---
  • Define the outgoing email properties. We are going to be using
  • CFHTTP post post the JSON (Javascript Object Notation) version
  • of these propreties to the PostMark API.
  • --->
  • <cfset emailSettings = {
  • to = "nat@natalie-gets-way-naughty.com",
  • from = "ben+from@bennadel.com",
  • subject = "PostMark Bounce Back Testing",
  • htmlBody = "<strong>All your emails are belong to us!</strong>"
  • } />
  •  
  • <!--- Post the email to the PostMark server. --->
  • <cfhttp
  • result="post"
  • method="post"
  • url="http://api.postmarkapp.com/email">
  •  
  • <!---
  • Alert the server that the we can accept JSON as the type of
  • data returned in the response.
  • --->
  • <cfhttpparam
  • type="header"
  • name="accept"
  • value="application/json"
  • />
  •  
  • <!---
  • Alert the server that the email content will be serialized
  • in the post body as JSON text.
  • --->
  • <cfhttpparam
  • type="header"
  • name="content-type"
  • value="application/json"
  • />
  •  
  • <!--- Define the API key to authorize post. --->
  • <cfhttpparam
  • type="header"
  • name="X-Postmark-Server-Token"
  • value="#request.apiKey#"
  • />
  •  
  • <!---
  • Post the serialized JSON email properties as the HTTP
  • message body.
  • --->
  • <cfhttpparam
  • type="body"
  • value="#serializeJSON( emailSettings )#"
  • />
  •  
  • </cfhttp>
  •  
  •  
  • <!--- Output the post response (returned in JSON format). --->
  • <cfdump
  • var="#deserializeJSON( post.fileContent )#"
  • label="PostMark CFHTTP Response"
  • />

As you can see, I first define my email settings in a ColdFusion structure. Then, when I invoke the Postmark API, I simply serialize the email settings using the serializeJSON() method and post the resultant JSON string as the CFHTTP body content. The Postmark API then returns a JSON response, which I can deserialize using deserializeJSON(). When we run the above code, we get the following API response:

 
 
 
 
 
 
Sending Emails From ColdFusion Using The Postmark Web Service API. 
 
 
 

Postmark has never seen that email address before - nat@natalie-gets-way-naughty.com - so it can offer us no insight at this time. As far as we know, Postmark has successfully sent out the given email. Of course, since we know that this email address is not valid, Postmark will eventually get a hard bounce back. Once Postmark has the hard bounce back on file, it can start to give us some more comprehensive feedback at "Send" time. If we wait a minute and re-execute the code above, we'll get the following output:

 
 
 
 
 
 
Sending Emails From ColdFusion Using The Postmark Web Service API Can Provide  
 
 
 

As you can see, the API call still executed without error; but, this time, Postmark knows that the given email address has previously caused a hard bounce back and will let us know that the given email address is not active. I am not 100% sure if this feedback is purely for user-insight; or, if it means that the given email was not sent.

Using the Postmark API to check for bounce backs is just as easy; all we need is the API key, some filtering, and the CFHTTP tag. It looks like Postmark keeps all bounce backs on file; as such, when you ask for bounce back information, you have to request it in a quasi-paginated manner using a page size and offset index. When doing so, the most recent bounce backs are listed first (offset zero).

In the following code, we are going to check to see if the above email address - nat@natalie-gets-way-naughty.com - caused a bounce back:

  • <!---
  • Get the bounce-back information from the PostMark server. When
  • doing this, we have a number of possible filters. For our use,
  • we're just gonna filters on email LIKE'ness.
  • --->
  • <cfhttp
  • result="get"
  • method="get"
  • url="http://api.postmarkapp.com/bounces">
  •  
  • <!---
  • Alert the server that the we can accept JSON as the type of
  • data returned in the response.
  • --->
  • <cfhttpparam
  • type="header"
  • name="accept"
  • value="application/json"
  • />
  •  
  • <!--- Define the API key to authorize post. --->
  • <cfhttpparam
  • type="header"
  • name="X-Postmark-Server-Token"
  • value="#request.apiKey#"
  • />
  •  
  • <!---
  • Pass in the email filter. Notice that we are filtering on
  • the *partial* email address. You don't have to do this - you
  • can use the full email address.
  • --->
  • <cfhttpparam
  • type="url"
  • name="emailFilter"
  • value="nat@natalie-gets-way-naughty"
  • />
  •  
  • <!---
  • Pass in the number of bounce backs that we want to list
  • (PostMark provides implicit pagination of all bounce-back
  • records, starting with the most recent first).
  • --->
  • <cfhttpparam
  • type="url"
  • name="count"
  • value="1"
  • />
  •  
  • <!---
  • Pass in the paging offset (which bounce back index will
  • start the given page) - zero is the first page.
  • --->
  • <cfhttpparam
  • type="url"
  • name="offset"
  • value="0"
  • />
  •  
  • </cfhttp>
  •  
  •  
  • <!---
  • Deserialize the response JSON. This should give us a structure
  • that contains the returned bounces plus the total number of
  • bounces in the system (returned or otherwise) that match the
  • given set of filtering criteria.
  • --->
  • <cfset response = deserializeJSON( toString( get.fileContent ) ) />
  •  
  • <!--- Output the bounce back response. --->
  • <cfdump
  • var="#response#"
  • label="PostMark Bounce Backs"
  • />

As you can see, I am searching for emails that are LIKE "nat@natalie-gets-way-naughty;" I left off the ".com" just to demonstrate that this filtering does not use exact text matching. As with the previous CFHTTP example, the request and response are both transferred in JSON format. When we get the Postmark API response, we need to deserialize the JSON string before we can use it. Running the above code, we get the following CFDump output:

 
 
 
 
 
 
The Postmark API Provides Comprehensive Bounce Back Informaiton Based On Your Email Queries. 
 
 
 

As you can see, the email address we were searching for did, indeed, generate a hard bounce back.

The Postmark API seems like a very powerful and easy way to send emails and monitor their delivery status. I'm still feeling my way around parts of the bounce back API; but, the Postmark support staff has been both very fast at responding and very open to new ideas regarding their functionality.




Reader Comments

Thanks Ben! I wanted to follow up regarding "I am not 100% sure if this feedback is purely for user-insight; or, if it means that the given email was not sent."

Right now we don't record the sent/pending status of an email in the API. This is coming in a few weeks though. You will be able to see if an email is sent or bounced at any given time.

Chris

Reply to this Comment

@Chris,

Sounds good. After some more testing, it looked like sending subsequent emails to the inactive email address did not generate further bounce-backs (at least based on the bounce back timestamp). So, I guess the API is smart enough to know now to waste its own time.

Reply to this Comment

You'd probably also like the emailParse CFC by Tom Mollerus: http://www.mollerus.net/tom/projects/emailParseCFC/

We've got this (in somewhat modified form) running as a scheduled task to deal with bounces from our newsletter send outs. Saving soft bounces for later processing, and unsubscribing all hard bounces automatically. In addition we're also using it to process those that reply with 'unsubscribe' in the subject.

The only problem I've ever run into is some hotmail reply that it can't handle, but I'm placing the blame for that one in M$FT's court!

Reply to this Comment

@Sam,

If I can ask, how are you dealing with the bounce-backs? Are you just grabbing the TO email? Or are you parsing out more in the header? This morning, I just experimented with Postmarks and parsing out custom headers (X-Customer-ID). But, I am not sure how much I can actually depend on such headers to be returned in the bounce back.

@Aaron,

It seems pretty slick. Still experimenting.

@Tom,

I'll take a look at that. I think they actually linked to that on the Postmark resources list, but I didn't have a chance to look at it just yet.

Reply to this Comment

@Ben basically it processes the bounces and splits out the originalTo, currentFrom, currentTo (this one is usually overkill), bouncetype, subject, body and UID.

The scheduled task takes a first stab at the emails, unsubscribing all that have a bouncetype 'hard'. Anything other than hard gets saved into a db table, so this would be the 'soft bounces' and 'unknown' (the actual replies with UNSUBSCRIBE as subject fall under this, but also some out of offices etc). As the cfc is right there, I've also adjusted this over time to deal with other typical status codes that should be hard but are producing soft etc.

We then run another batch of queries on this table to trim it down, unsubscribe the ones that have that in the subject etc.

Reply to this Comment

Thanks for the article, always looking for ways to improve email stability. I have also found issues in using the failTo attribute to find email that has bounced.

I have not been able to find an API for gmail services, but would anyone know where to find a list of supported and unsupported elements.

Reply to this Comment

@Sam,

And are just originalTo, bouncetype, and UID from the body of the email? Or is a combination of body and headers? I'm just trying to get a sense of what information people find is there on a dependable basis.

@Bryan,

What do you mean an API for GMail? They have POP and IMAP functionality that you can connect to.

Reply to this Comment

It's a combination, ie. subject is partly used to deduce bouncetype, as are specific codes that are included in the body that is returned. So far we've been able to always retrieve the fields we're after, although the originalTo can be a little messed up at times (wrong original address, or there is forwarding involved etc.) depending on the mail server. I think that's just the kind of thing you pretty much have to live with though :(

Reply to this Comment

Great post! I must say you have done a great job by providing this valuable information. I really appreciate your work.

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.