How to Send and Receive Emails with Go? 

On December 13, 2022
13min read
Piotr Malek Technical Content Writer @ Mailtrap
sending emails with Go

Turning 13 this year, Go (a.k.a. Golang) is the fourth most-desired back-end programming language. It was created in Google labs to address the shortcomings of other existing languages. It’s mostly based on C, but it also incorporates the ease of use of Python and JavaScript. 

Whether you’re building an app or your own server in Go, the chances are you’ll need to enable email sending and receiving to connect to your users. There are a few ways to achieve this, with some being more useful than others. 

We’ve put together a simple tutorial on sending emails in Golang with different approaches and code examples. Let’s Go!

How to send emails in Go with the net/smtp package?

The easiest way to send emails in Go is to use the SMTP package located within the net package.

SMTP (Simple Mail Transfer Protocol) is used to send emails by moving messages between email clients and mail servers. Golang also relies on this protocol and offers a basic and easy way to add email functionality to a Go app by implementing a library. The smtp package follows RFC 5321 and implements SMTP protocol accordingly. 

The main function of this package is SendMail with the following syntax:

func SendMail(addr string, a Auth, from string, to []string, msg []byte) error 

It lets you connect to a server (‘addr’ attribute, which must include a port number — for example, smtp.gmail.com:587). You can then specify the authentication method (more about that below), the sender’s and recipient’s email addresses, and the HTML that will be included in the message. Finally, you should clarify what will happen should an error occur.

To authenticate, you can use either of the two available authentication mechanisms:

  • Plain authentication
  • Cram-MD5 authentication

Plain authentication is the most basic method of authorizing an email client to act on your behalf. You provide your username and password, which are encoded with the basic base64 method. In Golang, this method is represented by the PlainAuth function and comes in the following shape:

func PlainAuth(identity, username, password, host string) Auth 

Cram-MD5 is a little more sophisticated. Using the provided credentials, it utilizes the challenge-response mechanism to authenticate with a server. It adds an extra layer of security and, although it comes with plenty of weaknesses, it’s still a more secure option than PlainAuth. The CRAMMD5Auth function comes with the following syntax:

func CRAMMD5Auth(username, secret string) Auth

The “secret” used in the previous code is either the user’s password or a hash of it.

Here’s what a sample code would look like if you were to send an email with Gmail SMTP and SendMail function: 

package main

import (

"log"

"net/smtp"

)

func main() {

// Choose auth method and set it up

auth := smtp.PlainAuth("", "john.doe@gmail.com", "extremely_secret_pass", "smtp.gmail.com")

// Here we do it all: connect to our server, set up a message and send it

to := []string{"kate.doe@example.com"}

msg := []byte("To: kate.doe@example.com\r\n" +

"Subject: Why aren’t you using Mailtrap yet?\r\n" +

"\r\n" +

"Here’s the space for our great sales pitch\r\n")

err := smtp.SendMail("smtp.gmail.com:587", auth, "john.doe@gmail.com", to, msg)

if err != nil {

log.Fatal(err)

}

}

The code sample above uses PlainAuth to authenticate

Since May 2022, Google doesn’t support less secure app access which was necessary to use the SendMail function with Gmail accounts. The alternative is to enable 2-step verification and use an app password for authentication. You can learn how to create and use app passwords here

Once you generate the app password, you should use it instead of your regular Gmail password whenever required. In the code sample above, it would substitute extremely_secret_pass in the auth section: 

auth := smtp.PlainAuth("", "john.doe@gmail.com", "app_password", "smtp.gmail.com")

Remember: don’t use actual passwords in the code. Even if you’re just playing around or testing the app, it’s reasonable to change these values with environmental variables. You can load them later with os.Getenv.

Several things to note: the SendMail function will automatically use TLS encryption if the server supports it — any major SMTP server will do so. Also, SendMail doesn’t offer support for DKIM authentication or MIME attachments. And one more thing, the net/smtp package doesn’t accept new features as it’s frozen. 

Luckily, there are alternative methods that support authentication protocols and attachments. 

How to send emails with Gomail?

To send emails in Go with SMTP you could also use the Gomail package. While net/smtp is Go’s native package, Gomail is community-driven. It addresses the limitations we’ve described above by supporting text/HTML templates, attachments, embedded images, and automatic encoding of special characters. 

package main

import (

"github.com/go-mail/mail"

)

func main() {

m := mail.NewMessage()

m.SetHeader("From", "john.doe@gmail.com")

m.SetHeader("To", "kate.doe@example.com", "noah.doe@example.com")

m.SetAddressHeader("Cc", "oliver.doe@example.com", "Oliver")

m.SetHeader("Subject", "Hello!")

m.SetBody("text/html", "Hello <b>Kate</b> and <i>Noah</i>!")

m.Attach("lolcat.jpg")

d := mail.NewDialer("smtp.gmail.com", 587, "john.doe@gmail.com", "123456")

// Send the email to Kate, Noah and Oliver.

if err := d.DialAndSend(m); err != nil {

panic(err)

}

}

The main functions Gomail uses are NewDialer (initiates a new SMTP connection), NewMessage (creates a new message), and Send (sends the message). 

Gomail also has the function Attach which makes it possible to send emails with attachments (you know, the images of your cat you include with every single email). We’ll talk about attachments in more detail later. Here’s what func Attach looks like:

func (m *Message) Attach(filename string, settings ...FileSetting) 

You’ll just need to specify the filename and its type as in the example below: 

m.Attach("lolcat.jpg")

Unlike net/smtp package, Gomail mail module doesn’t require to specify authentication methods. By default, it uses the LOGIN mechanism and switches to CRAM-MD5 whenever possible. More information on Gomail is available on Github

How to send emails via Golang and third-party Email API?

To send emails via Golang with a third-party Email API, you have two main options: 

  • Use the Email API as an SMTP email server; 
  • Integrate it in your Golang app using the API key and token. 

While there’s nothing wrong with using net/smtp or Gomail packages for sending emails, they have certain limitations. They aren’t ideal for sending transactional emails in bulk. Both of these packages leverage existing SMTP servers or require you to build your own to handle the traffic. 

In some cases, setting up a local SMTP server is a more viable option, but it can compromise the email sender reputation and reduce email deliverability. For that reason, using third-party Email APIs is the best solution for sending high volumes of emails. 

Now let’s see how that’s done with Golang. 

Sending emails with a third-party SMTP server

To send emails with a third-party SMTP server, you can use Mailtrap Email API – a sending solution that allows you to reach recipients’ inboxes while providing more control over your deliverability. 

The process will look the same as with the net/smtp package, but before getting started, you’d need to verify your domain and get SMTP credentials. 

Here’s how: 

  • Create a Mailtrap account and log in; 
  • On the left-side panel, press Email API and expand the category Sending Domains; 
  • Type in your domain name, press Add Your Domain, and follow the verification steps;
  • Once the domain is verified, navigate to the tab “API and SMTP”. Press SMTP, copy your credentials, and paste them into your Go app. 

If everything is done correctly, your code should look like the one below with your credentials in it: 

package main

import (

"log"

"net/smtp"

)

func main() {

// Mailtrap account config

username := "api"

password := "<secret_token>"

smtpHost := "send.smtp.mailtrap.io"

// Choose auth method and set it up

auth := smtp.PlainAuth("", username, password, smtpHost)

// Message data

from := "john.doe@your.domain"

to := []string{"kate.doe@example.com"}

message := []byte(`To: kate.doe@example.com

From: john.doe@your.domain

Subject: Why aren’t you using Mailtrap yet?

Here’s the space for your great sales pitch`)

// Connect to the server and send message

smtpUrl := smtpHost + ":25"

err := smtp.SendMail(smtpUrl, auth, from, to, message)

if err != nil {

log.Fatal(err)

}

}

At this point, you’ll be able to run the code and send a test email. If the attempt is successful, verify your setup in your Mailtrap account. 

You should be able to see your test email in Email Logs within minutes. This means that you can now use Mailtrap Email API to send bulk emails from your Go app or project.

Sending emails with Email API through API integration.

If using SMTP to send emails via Golang isn’t an option for you, you can always integrate the Mailtrap Email API into the code of your app with the API integration. The process is similar to the SMTP integration we described above, but this time, you have to pick the API in the “API and SMTP” tab. Choose Go from the dropdown menu, and you’ll immediately see a sample code. 

Once you modify the values of “from”, “to”, “subject”, “headers”, “content”, and fill in the empty strings, the code will look like this:

package main

import (

"bytes"

"fmt"

"io"

"log"

"net/http"

"time"

)

func main() {

// Mailtrap account config

token := "<secret_token>"

httpHost := "https://send.api.mailtrap.io/api/send"

// Message body

message := []byte(`{"from":{"email":"john.doe@your.domain"},

"to":[{"email":"kate.doe@example.com"}],

"subject":"Why aren’t you using Mailtrap yet?",

"text":"Here’s the space for your great sales pitch"}`)

// Set up request

request, err := http.NewRequest(http.MethodPost, httpHost, bytes.NewBuffer(message))

if err != nil {

log.Fatal(err)

}

// Set required headers

request.Header.Set("Content-Type", "application/json")

request.Header.Set("Authorization", "Bearer "+token)

// Send request

client := http.Client{Timeout: 5 * time.Second}

res, err := client.Do(request)

if err != nil {

log.Fatal(err)

}

defer res.Body.Close()

body, err := io.ReadAll(res.Body)

if err != nil {

log.Fatal(err)

}

fmt.Println(string(body))

}

Run the code to check if you did everything correctly. If so, you should verify the setup to start sending emails from Golang. After that, you’ll also be able to use other features of the Mailtrap Email API, such as tracking, actionable analytics, and 60 days of email logs. 

How to send HTML emails in Go?

To send engaging and responsive HTML emails in Go, there are two main options: 

  • Use HTML as the body of the message;
  • Use Gomail package;

The first option is based on the idea that most SMTP clients can format HTML themselves. All we have to do is let the recipient’s SMTP client know that it’s receiving an HTML email. To do so, we have to update the Content-Type header and send HTML as the message body:

headers := map[string]string{

"From": "john.doe@gmail.com",

"To": "kate.doe@example.com",

"Subject": "Why aren’t you using Mailtrap yet?",

"Content-Type": "text/html; charset=utf-8",

}

If the Content-Type header is omitted, all the HTML tags will render as plain text. 

Alternatively, you can just use the Gomail package for HTML emails. In the code sample we provided above, the relevant section looks like this: 

m.SetBody("text/html", "Hello <b>Kate</b> and <i>Noah</i>!") 

Gomail also allows you to attach a plain text version to your HTML message for clients who don’t support HTML. This can be done using the AddAlternative function: 

func (m *Message) AddAlternative(contentType, body string, settings ...PartSetting)

Remember, that plain text should be added before the HTML:

m.SetBody("text/plain", "Hello!")

m.AddAlternative("text/html", "<p>Hello!</p>")

HTML emails can be sent with any Go library that supports HTML

The great thing about Go is that it supports HTML templates with its html/template package. This means that you can build responsive HTMLs to send to your users. There are several tutorials on building templates in Go; in our opinion, this one seems really interesting.

How to send email with attachments using Golang?

Apart from the function Attach in Gomail, it’s possible to send email with attachments in Go with the mime/multipart method. MIME is the standard for sending various types of data (files, images, videos, etc.) via email. It encapsulates the body of the message and all the attachments in the multipart message. MIME uses base64 encoding to convert binary text into ASCII type text. 

Sending email with attachments with the mime/multipart method is far from straightforward. It requires the specification of multipart type, body part, and content disposition. That’s why devs prefer to use 3rd-party libraries that simplify that process. Jordan Wright’s “email” library is particularly useful. 

How to send emails to multiple recipients in Go?

Sending an email to a single recipient is fairly simple, but what if we have several people in mind? Luckily, this is also really easy to set up. Let’s cut a piece from the net/smtp code sample and focus solely on the message headers and email body:

to := []string{"kate.doe@example.com"}

msg := []byte("To: kate.doe@example.com\r\n" +

"Subject: Why aren’t you using Mailtrap yet?\r\n" +

"\r\n" +

"Here’s the space for our great sales pitch\r\n")

Notice how the recipient’s email address is placed in both the headers and the body. To send emails to more than one recipient, we’ll need to add them to both parts, separated by a comma.

to := []string{"kate.doe@example.com", “noah.doe@example.com}

msg := []byte("To: kate.doe@example.com, noah.doe@example.com \r\n" +

"Subject: Why you’re not using Mailtrap yet?\r\n" +

"\r\n" +

"Here’s the space for our great sales pitch\r\n")

Now, let’s assume this email is mainly addressed to Kate Doe, but we want Noah Doe to be cc’ed.

to := []string{"kate.doe@example.com"}

cc:= []string{“noah.doe@example.com”}

msg := []byte("To: kate.doe@example.com\r\n" +

“cc: noah.doe@example.com\r\n"

"Subject: Why you’re not using Mailtrap yet?\r\n" +

"\r\n" +

"Here’s the space for our great sales pitch\r\n")

Finally, let’s imagine we don’t want Kate to know that Noah knows. This is done by adding Noah as bcc to the function parameters but excluding him from the message headers.

to := []string{"kate.doe@example.com", “noah.doe@example.com}

msg := []byte("To: kate.doe@example.com\r\n" +

"Subject: Why aren’t you using Mailtrap yet?\r\n" +

"\r\n" +

"Here’s the space for our great sales pitch\r\n")

Testing emails before sending in Go: why and how? 

Now you know the different approaches to sending emails with Golang. You might be tempted to skip the testing part and start sending messages right away. But before you do that, you should test your emails to ensure they don’t end up in the spam folder or get discarded. You should also check HTML/CSS if you’re using complex HTML templates. 

Mailtrap Email Sandbox is a time-saving solution that traps all the SMTP traffic from staging. It provides Spam Analysis and HTML check, among other features.

Try Mailtrap for Free

The Spam analysis feature checks your emails for Spam Score and Blacklists. It uses Apache SpamAssassin filter to determine the spam score of your messages. Each component of the spam analysis is displayed next to the color-coded score chart. 

If the spam score is above 5 points, you’ll easily find the culprit and correct the mistakes on the go. The Email Sandbox will also check your domains against the most common blacklists and display the results under the Blacklists Report tab. 

Apart from Spam Analysis, Mailtrap Email Sandbox also provides a comprehensive HTML check. It examines your email to find problematic elements. Then it displays results based on the email client support. You can press the numbers alongside unsupported clients to open the Notes tab with detailed explanations and view problematic lines of code in the HTML Source window. 

With Email Sandbox, you can easily manage your testing process and share it with other team members, create a new project and add multiple inboxes within it. 

To start testing emails in Go, navigate to the Sandbox tab, select an inbox, press SMTP settings, and open Show Credentials. You’ll immediately see the credentials of Mailtrap’s fake SMTP server.

Copy the credentials and integrate them into the Go app.  The code will be similar to the one we described while talking about using Mailtrap Email API as an SMTP. The only difference would be in the func main ()  { section: 

// Mailtrap account config

username := "<inbox_username>"

password := "<secret_password>"

smtpHost := "smtp.mailtrap.io"

Once you run the code, you’ll be able to start sending test emails from Golang to the virtual inbox of the Mailtrap Email Sandbox. 

How to receive emails in Golang?

Just as with sending, receiving emails is also quite simple in Golang. You can use simple go-pop3 and go-imap packages or leverage the external packages created by the members of the community. 

To build a POP3 client, first you’ll need to install the go-pop3 package with the go get command:

go get -u github.com/knadh/go-pop3

Once the package is installed, building the POP3 client is an easy task. For that, you’ll need to specify the host and the port. Then create a new connection with p.NewConn(). Remember that POP3 connections are stateful, meaning that they remember the client data. So, you should always close the connection with Quit() once you’re done. 

Go-pop3 package allows you to read one (func (*Conn) ReadOne) or all (func (*Conn) ReadAll) lines of connection and pull all messages from the server (fmt.Println). The messages can be deleted from the server only after the successful Quit ()

package main

import (

"fmt"

"log"

"github.com/knadh/go-pop3"

)

func main() {

// Initialize the client.

p := pop3.New(pop3.Opt{

Host: "pop.gmail.com",

Port: 995,

TLSEnabled: true,

})

// Create a new connection. POP3 connections are stateful and should end

// with a Quit() once the operations are done.

c, err := p.NewConn()

if err != nil {

log.Fatal(err)

}

defer c.Quit()

// Authenticate.

if err := c.Auth("myuser", "mypassword"); err != nil {

log.Fatal(err)

}

// Print the total number of messages and their size.

count, size, _ := c.Stat()

fmt.Println("total messages=", count, "size=", size)

// Pull the list of all message IDs and their sizes.

msgs, _ := c.List(0)

for _, m := range msgs {

fmt.Println("id=", m.ID, "size=", m.Size)

}

// Pull all messages on the server. Message IDs go from 1 to N.

for id := 1; id <= count; id++ {

m, _ := c.Retr(id)

fmt.Println(id, "=", m.Header.Get("subject"))

// To read the multipart e-mail bodies, see:

// https://github.com/emersion/go-message/blob/master/example_test.go#L12

}

// Delete all the messages. Server only executes deletions after a successful Quit()

for id := 1; id <= count; id++ {

c.Dele(id)

}

}

Like POP3, you’ll need to download the go-imap package to build the IMAP client. To do so, you’ll need to start a new connection and specify the number of messages you want to receive. You can access all mailboxes with the PrintIn function and select a specific inbox with c.Select. You can also choose the number of messages you want to access and close the connection with log.Println("Done!").

package main

import (

"log"

"github.com/emersion/go-imap/client"

"github.com/emersion/go-imap"

)

func main() {

log.Println("Connecting to server...")

// Connect to server

c, err := client.DialTLS("mail.example.org:993", nil)

if err != nil {

log.Fatal(err)

}

log.Println("Connected")

// Don't forget to logout

defer c.Logout()

// Login

if err := c.Login("username", "password"); err != nil {

log.Fatal(err)

}

log.Println("Logged in")

// List mailboxes

mailboxes := make(chan *imap.MailboxInfo, 10)

done := make(chan error, 1)

go func () {

done <- c.List("", "*", mailboxes)

}()

log.Println("Mailboxes:")

for m := range mailboxes {

log.Println("* " + m.Name)

}

if err := <-done; err != nil {

log.Fatal(err)

}

// Select INBOX

mbox, err := c.Select("INBOX", false)

if err != nil {

log.Fatal(err)

}

log.Println("Flags for INBOX:", mbox.Flags)

// Get the last 4 messages

from := uint32(1)

to := mbox.Messages

if mbox.Messages > 3 {

// We're using unsigned integers here, only subtract if the result is > 0

from = mbox.Messages - 3

}

seqset := new(imap.SeqSet)

seqset.AddRange(from, to)

messages := make(chan *imap.Message, 10)

done = make(chan error, 1)

go func() {

done <- c.Fetch(seqset, []imap.FetchItem{imap.FetchEnvelope}, messages)

}()

log.Println("Last 4 messages:")

for msg := range messages {

log.Println("* " + msg.Envelope.Subject)

}

if err := <-done; err != nil {

log.Fatal(err)

}

log.Println("Done!")

}

Wrapping up

This wraps up our guide on sending emails with Golang. As you can see, the choice comes down to either utilizing the native packages and functionalities of Go or connecting to 3rd party libraries or SMTP servers such as Mailtrap Email API

Remember to test your emails before sending them to your users. With Mailtrap Email Sandbox, devs automate testing workflow, capture SMTP traffic from staging, and debug emails with HTML/CSS check. 

If you’re interested in sending and receiving emails in other frameworks, check out our blog posts about C#, ASP.NET C#, Python, PHP, or Ruby on Rails. Stay tuned to Mailtrap blog for our upcoming tutorial on sending emails with ASP.NET Core. 

Article by Piotr Malek Technical Content Writer @ Mailtrap