JSON-Postmark trials
Recently, Postmark became less forgiving about the stuff people were sending them. I had been using some code 8 years ago from gitHub that prepared and sent email templates via Postmark. I've just dragged that code out again and it is erroring all over town right now.
I really hope I've provided enough detail and also hope someone is patient enough to trawl through it. In places throughout the code, I've added comments like "<!--- SEE OUTPUT #2 --->". These refer to various dumps I've added at the bottom to let you see the results of the processing.
I also ran some tests using simpler HTML and even plain text, the results of which I've also included at the bottom. Basically, everything I've tried results in the same Postmark error:
{""ErrorCode"":403,""Message"":""Invalid request field(s): $.HTMLBody""}So, at this point I'm concluding that the formatting of the JSON for Postmark consumption is going wrong somewhere.
If you need any more information, please ask.
My processing goes as follows:
Step 1: User shows interest in a trade-related job and clicks "Chase Job". This calls a function that generates the appropriate email template, much of the content stored in a database, then sends it off to the Postmark API cfc.
<cffunction name="SendJobStatusEmail" access="remote" returnType="boolean">
.....
<cfswitch expression = "#arguments.status#">
<cfcase value="2">
<cfset vSubject = "Job Interest">
<cfset vIntro = getTemplates(name="Intro",job_status=1,entity="users").data>
<cfset vSecondaryText = getTemplates(name="SecondaryText",job_status=2).data>
<cfset vButtonText = 'View Tradies'>
<cfset vPostButtonText = ''>
<cfset vButtonRedir = urlEncodedFormat(siteroot & vJobLink & '...)>
</cfcase>
....
</cfswitch>
...
<cfsavecontent variable="emailBody">
<cfoutput>#qTemplate.data#</cfoutput> <!--- SEE OUTPUT #1 --->
</cfsavecontent>
....
<cfset resPostmark = oPostmark.sendMail(
mailTo = qJob.recipient_email
,mailFrom = application.fromEmail
,mailSubject = vSubject
,apiKey = application.postmarkAPIKey
,mailHTML = emailBody
)>
....
</cfunction>
Step 2: Postmark API receives these arguments and processes them as follows:
<cfcomponent name="PostMarkAPI">
<cffunction name="sendMail" access="public" returntype="string" description="Create JSON packet -> Postmarkapp">
<cfargument name="mailTo" required="yes" type="string"/>
<cfargument name="mailFrom" required="yes" type="string"/>
<cfargument name="mailSubject" required="yes" type="string"/>
<cfargument name="apiKey" required="yes" type="string"/>
<cfargument name="mailReply" required="no" type="string"/>
<cfargument name="mailCc" required="no" type="string"/>
<cfargument name="mailHTML" required="no" type="string" />
<cfargument name="mailTxt" required="no" type="string"/>
<cfset var vApiURL = "http://api.postmarkapp.com/email" />
<cfset var vmailto = JSStringFormat(arguments.mailTo) />
<cfset var vmailFrom = JSStringFormat(arguments.mailFrom) />
<cfset var vmailSubject = JSStringFormat(arguments.mailSubject) />
<cfset var vmailCc = '' />
<cfset var vmailHTML = '' />
<cfset var vmailTxt = '' />
<cfset var vmailReply = '' />
<cfset vmailCc = JSStringFormat(arguments.mailCc) />
<cfset vmailHTML = arguments.mailHTML.reReplaceNoCase(">\s",">","all").reReplaceNoCase("\s<","<","all")>
<cfset vmailHTML = JSStringFormat(arguments.mailHTML) />
<cfset vmailTxt = JSStringFormat(arguments.mailTxt) />
<cfset vmailReply = JSStringFormat(arguments.mailReply) />
<cfset vmailCc = arguments.mailCc />
<cfset vmailTxt = arguments.mailTxt />
<cfset vmailReply = arguments.mailReply />
<cflog file="ace" text="#vmailHTML#"> <!--- SEE OUTPUT #2 --->
<cfsavecontent variable="jsonPacket">
<cfprocessingdirective suppressWhiteSpace="yes">
<cfoutput>
{
"From" : "#vmailFrom#",
"To" : "#vmailto#",
<cfif len(trim(vmailCc))>"Cc" : "#vmailCc#",</cfif>
"Subject" : "#vmailSubject#"
<cfif len(trim(vmailHTML))>, "HTMLBody" : "#vmailHTML#"</cfif>
<cfif len(trim(vmailTxt))>, "TextBody" : "#vmailTxt#"</cfif>
<cfif len(trim(vmailReply))>, "ReplyTo" : "#vmailReply#"</cfif>
}
</cfoutput>
</cfprocessingdirective>
</cfsavecontent>
<cflog file="ace" text="#jsonPacket#"> <!--- SEE OUTPUT #3 --->
<cfhttp url="https://api.postmarkapp.com/email" redirect="yes" method="post" >
<cfhttpparam type="header" name="Accept" value="application/json" />
<cfhttpparam type="header" name="Content-type" value="application/json" />
<cfhttpparam type="header" name="X-Postmark-Server-Token" value="#arguments.apiKey#" />
<cfhttpparam type="body" encoded="no" value="#jsonPacket#" />
</cfhttp>
<cfreturn cfhttp.responseHeader.Status_Code />
<cflog file="aceoftradesErrors" text="#cfhttp.fileContent#"> <!--- SEE OUTPUT #4 --->
....
</cffunction>
</cfcomponent>If I run bare bones HTML through, such as the following
<cfsavecontent variable="emailBody">
<html><div> <p>Hi there. How's it going?</p> </div> </html>
</cfsavecontent>
It builds a jasonPacket:
{ ""From"" : ""info@vwxyz.co.nz"", ""To"" : ""info@vwxyz.co.nz"", ""Subject"" : ""Good news - you have interest in your job"" , ""HTMLBody"" : ""\r\n \r\n \r\n <html><div><p>Hi there. What\'s happening?</p></div></html>\n "" }(Note: All the "\r\n" stuff comes from the JSStringFormat() in the PostMarkAPI cfc. It doesn't make any difference to the result if I remove that.)
and throws a cfhttp.fileContent error:
{""ErrorCode"":403,""Message"":""Invalid request field(s): $.HTMLBody""}
If I run bare bones HTML through, such as the following
<cfsavecontent variable="emailBody">
<html><div> <p>Hi there. How's it going?</p> </div> </html>
</cfsavecontent>or plain text
<cfsavecontent variable="emailBody">
Hi there, what's the happs?
</cfsavecontent>
I get the same results
OUTPUT DUMPS
----------------------
1)
<div style=""margin:0em auto;padding:0em;width:600px;min-width:600px""><div style=""padding:1.25em 50px;text-align:center""><a href=""#application.siteroot#/?utm_campaign=transactional&utm_source=emails.logo&utm_medium=email""><img src=""http://aceoftrades/images/website/logo10-180.png""></a></div><div align=""center"" style=""margin:0em auto;padding:0em;background-color:##fff;width:600px;min-width:600px;border-radius:3px""><div style=""width:550px;min-width:550px;padding-top:0.5em;padding-bottom:1em""><div style=""font-size:15px;text-align:left;font-family:Arial,Helvetica,sans-serif;color:##666""><p style=""font-family:'Kameron';font-size:130%;padding-top:1.25em;padding-bottom:0em;margin:0;font-weight:bold"">Hi <a rel=""nofollow"" style=""text-decoration:none;color:##666"">#qJobDetails.recipient_name#</a>,</p><div style=""padding-bottom:1em""><p>#vIntro#</p><p>#vSecondaryText#</p><cfif len(trim(vButtonText))><div style=""text-align:center;padding:1.5em 0.5em""><a href=""#application.siteroot#/track_click.cfm?type=email&object=job_link&subject=job_alert&redir=#vJobLink#"">#vButtonText#</a></div></cfif><cfif len(trim(vSecondaryText))><p>#vSecondaryText#</p></cfif></div></div></div></div><div style=""padding:1.5em 50px 0 50px""><div style=""color:##999;font-size:85%;text-align:center"">Delivered to <span style=""text-decoration:none""><span style=""color:##555""><a href=""mailto:#qJobDetails.recipient_email#"" target=""_blank"">#qJobDetails.recipient_email#</a></span></span><br>© #year(now())# #application.author# | <a href=""#application.siteroot#/contact?subject=bug"">report an issue</a></div></div></div>
2)
<div style=\""margin:0em auto;padding:0em;width:600px;min-width:600px\""><div style=\""padding:1.25em 50px;text-align:center\""><a href=\""#application.siteroot#/?utm_campaign=transactional&utm_source=emails.logo&utm_medium=email\""><img src=\""http://aceoftrades/images/website/logo10-180.png\""></a></div><div align=\""center\"" style=\""margin:0em auto;padding:0em;background-color:##fff;width:600px;min-width:600px;border-radius:3px\""><div style=\""width:550px;min-width:550px;padding-top:0.5em;padding-bottom:1em\""><div style=\""font-size:15px;text-align:left;font-family:Arial,Helvetica,sans-serif;color:##666\""><p style=\""font-family:\'Kameron\';font-size:130%;padding-top:1.25em;padding-bottom:0em;margin:0;font-weight:bold\"">Hi<a rel=\""nofollow\"" style=\""text-decoration:none;color:##666\"">#qJobDetails.recipient_name#</a>,</p><div style=\""padding-bottom:1em\""><p>#vIntro#</p><p>#vSecondaryText#</p><cfif len(trim(vButtonText))><div style=\""text-align:center;padding:1.5em 0.5em\""><a href=\""#application.siteroot#/track_click.cfm?type=email&object=job_link&subject=job_alert&redir=#vJobLink#\"">#vButtonText#</a></div></cfif><cfif len(trim(vSecondaryText))><p>#vSecondaryText#</p></cfif></div></div></div></div><div style=\""padding:1.5em 50px 0 50px\""><div style=\""color:##999;font-size:85%;text-align:center\"">Delivered to<span style=\""text-decoration:none\""><span style=\""color:##555\""><a href=\""mailto:#qJobDetails.recipient_email#\"" target=\""_blank\"">#qJobDetails.recipient_email#</a></span></span><br>© #year(now())# #application.author# |<a href=\""#application.siteroot#/contact?subject=bug\"">report an issue</a></div></div></div>3) Without JSStringFormat:
{ ""From"" : ""info@vwxyz.co.nz"", ""To"" : ""info@vwxyz.co.nz"", ""Subject"" : ""Good news - you have interest in your job"" , ""HTMLBody"" : ""<div style=""margin:0em auto;padding:0em;width:600px;min-width:600px""><div style=""padding:1.25em 50px;text-align:center""><a href=""#application.siteroot#/?utm_campaign=transactional&utm_source=emails.logo&utm_medium=email""><img src=""http://aceoftrades/images/website/logo10-180.png""></a></div><div align=""center"" style=""margin:0em auto;padding:0em;background-color:##fff;width:600px;min-width:600px;border-radius:3px""><div style=""width:550px;min-width:550px;padding-top:0.5em;padding-bottom:1em""><div style=""font-size:15px;text-align:left;font-family:Arial,Helvetica,sans-serif;color:##666""><p style=""font-family:'Kameron';font-size:130%;padding-top:1.25em;padding-bottom:0em;margin:0;font-weight:bold"">Hi<a rel=""nofollow"" style=""text-decoration:none;color:##666"">#qJobDetails.recipient_name#</a>,</p><div style=""padding-bottom:1em""><p>#vIntro#</p><p>#vSecondaryText#</p><cfif len(trim(vButtonText))><div style=""text-align:center;padding:1.5em 0.5em""><a href=""#application.siteroot#/track_click.cfm?type=email&object=job_link&subject=job_alert&redir=#vJobLink#"">#vButtonText#</a></div></cfif><cfif len(trim(vSecondaryText))><p>#vSecondaryText#</p></cfif></div></div></div></div><div style=""padding:1.5em 50px 0 50px""><div style=""color:##999;font-size:85%;text-align:center"">Delivered to<span style=""text-decoration:none""><span style=""color:##555""><a href=""mailto:#qJobDetails.recipient_email#"" target=""_blank"">#qJobDetails.recipient_email#</a></span></span><br>© #year(now())# #application.author# |<a href=""#application.siteroot#/contact?subject=bug"">report an issue</a></div></div></div>"" }4)
cfhttp.fileContent: {""ErrorCode"":403,""Message"":""Invalid request field(s): $.HTMLBody""}
jsonlint error:
JSONLint Error
Error: Parse error on line 1:
{ ""From"" : ""info@vwxy
----^
