Groovy, Mailgun & "Fiancé"

This is a Tech Post written by our Co-Founder and Lead Engineer, Andrew Garcia. Have an idea for a tech post? Email us:


Here at Goodshuffle we've built our entire platform using Grails (currently 3.3.1) and occasionally we run into interesting problems that hopefully by writing down here we can help save others out there a boat load of time. Truth be told, we should've started the tech portion of our blog a long time ago; it's been a long journey.

Mailgun & HttpBuilder

Like I said we've been on Grails for a while (it's a tidy package around Spring, Hibernate, using Groovy along with Java that runs on the JVM) and ran into an issue using HttpBuilder to make API calls to Mailgun. Our function was structured to pass & encode attachments which forced us to use MultipartEntityBuilder to specify the 'parts'. And, for some reason, it took way longer than it should have to figure out how to properly set UTF-8 encoding.

This was a big issue because our platform is built for the event rental industry, where a lot of communication is between rental companies and brides & grooms, so the word "fiancé" is used frequently as you can imagine. We needed to have special characters display properly in the messages that arrived via email.

I read the docs, saw Mailgun's use of quoted-printable encoding and vanished into what felt like an unnecessary black hole of confusion. I attempted setting the content-type header in a billion places until finding/realizing where it was actually needed.

Anyhow, if you Googled "UTF-8 encoding Mailgun, Httpbuilder" and are looking for code on how to do it, here it is. Make sure you set the charset on the StringBody constructor call.

HTTPBuilder http = new HTTPBuilder("")http.request(Method.POST) { multipartRequest -> uri.path = "/v3/" + mailGunDomain + "/messages"

        headers["Authorization"] = "Basic " + ("api:" + System.env.MAILGUN_SECRET_API_KEY).bytes.encodeBase64().toString()

        MultipartEntityBuilder multipartRequestEntity = MultipartEntityBuilder.create()

        multipartRequestEntity.addPart('from', new StringBody(sourceEmailAddress))
        multipartRequestEntity.addPart('to', new StringBody(destinationEmail))
        multipartRequestEntity.addPart('h:Reply-To', new StringBody(replyToEmail))

        multipartRequestEntity.addPart('subject', new StringBody(title))
        multipartRequestEntity.addPart('html', new StringBody(content, org.apache.http.entity.ContentType.TEXT_HTML.withCharset('UTF-8')))

        attachments?.each { Map attachment ->
  "adding mime attachment name=" + + ", fileSize=" + ((byte[]) attachment.contents).length)
            multipartRequestEntity.addPart('attachment', new ByteArrayBody((byte[]) attachment.contents,

        multipartRequest.entity =

        response.success = { resp, json ->
  "Successfully sent e-mail to Mailgun for processing. resp.message=" + json.message +
                    ", messageID=" + + ", to_email=" + destinationEmail + ", conversationHash=" + conversationHash) 
        response.failure = { resp, jsonMap ->
            throw new RuntimeException("Error occurred while sending transactional e-mail to mailgun. title=" + title + ", source_email=" + sourceEmail + ", destinationEmail=" + destinationEmail)

        return true


Andrew Garcia

Andrew Garcia

Co-Founder & Lead Engineer of Goodshuffle. If you have any technical questions about launching marketplaces or SaaS platforms just ask! :)

  Washington, D.C.

You may also like

    Comments powered by Disqus