Sending HTML to Mailgun with cURL

October 25, 2015

I used to run Postfix and have it send my emails through Mailgun, mostly for the reasons explained in The Hostile Email Landscape.

But Mailgun accepts emails from a POST request, so I thought it would be a good opportunity to get rid of Postfix on my virtual private server.

Mailgun and cURL

On their main page, Mailgun shows you how to send an email with curl:

curl -s --user "api:$API-KEY" \ \
     -F from='' \
     -F to='' \
     -F subject='Hello' \
     -F text='how are you?'

That works fine, but it’s overly simplified. Here’s what I really need to know:

Some answers:

Based on their documentation and putting it all together, I tried this:

cat some.html | curl -s --user "api:$API-KEY" \ \
     -F from='' \
     -F to='' \
     -F subject='Hello' \
     -F html="@-"

but kept getting this error:

  "message": "'html' parameter is not a string"

It could have been easy: Mailgun could cover more (realistic) cases in their documentation. As it stands, even when they send HTML, it just a one-liner.

The Solution

I had never seen an example of this, but you can change @- to <-:

cat some.html | curl -s --user "api:$API-KEY" \ \
     -F from='' \
     -F to='' \
     -F subject='Hello' \
     -F html="<-"

The < means send the file ‘as text’, not as a file. The curl man page itself:

-F, --form <name=content>
   (HTTP) This lets curl emulate a filled-in form in which  a  user
   has  pressed  the  submit  button. This causes curl to POST data
   using the  Content-Type  multipart/form-data  according  to  RFC
   2388.  This  enables uploading of binary files etc. To force the
   'content' part to be a file, prefix the  file  name  with  an  @
   sign.  To just get the content part from a file, prefix the file
   name with the symbol <. The difference between @ and <  is  then
   that  @  makes a file get attached in the post as a file upload,
   while the < makes a text field and just  get  the  contents  for
   that text field from a file.

You can see the difference in a netcat capture of both commands:

at versus lt

Finally, I packaged this curl command in a script to replace both the postfix and the mailx-bsd packages.

> cat /usr/local/bin/send-with-mailgun    # replace variables with your values

curl --user "api:$API-KEY" \${MAILBOX}/messages \
  -F html="<-" "$@"

> cat some.html | send-with-mailgun -F subject="Hello" -F to="" -F from=""

Discussion, links, and tweets

Follow me on Twitter