Sending email over SMTP in go is easy. Go contains its own [net/smtp](https://pkg.go.dev/net/smtp) package but it can be cumbersome. Luckily there exists the excellent open source [emersion](https://github.com/emersion) [go-smtp package](https://github.com/emersion/go-smtp). In this post we will show you how to send email using SMTP commands in go.

If you searched for `golang smtp`, the practical path is: choose the SMTP library, configure TLS intentionally, send a controlled message, and verify the received result instead of trusting the send call alone.

For the transport background, see [SSL for email](/guides/ssl-for-email/) and [SMTP vs HTTP inboxes](/guides/smtp-vs-http-email-inboxes/).

## Install dependencies

Add the `github.com/emersion/go-smtp` dependency to your go project. For this example we will also add [MailSlurp](/api/) to create an SMTP inbox for our test. We can find this at `github.com/mailslurp/mailslurp-client-go`.



```go
import (
	"context"
	"github.com/antihax/optional"
	sasl "github.com/emersion/go-sasl"
	smtp "github.com/emersion/go-smtp"
	mailslurp "github.com/mailslurp/mailslurp-client-go"
	"github.com/stretchr/testify/assert"
	"log"
	"os"
	"strings"
	"testing"
)
//
```



Notice the `assert` package - we can use that to test our SMTP client code.

## Get MailServer connection details

We need to send email with Go to and through email mailservers. We can do this if we know a mailservers port and host names. With [MailSlurp](/api/) we can generate a new disposable mailbox and use the details associated with it.



```go
var apiKey = os.Getenv("API_KEY")

func getMailSlurpClient(t *testing.T) (*mailslurp.APIClient, context.Context) {
	assert.NotNil(t, apiKey)

	// create a context with your api key
	ctx := context.WithValue(context.Background(), mailslurp.ContextAPIKey, mailslurp.APIKey{Key: apiKey})

	// create mailslurp client
	config := mailslurp.NewConfiguration()
	client := mailslurp.NewAPIClient(config)

	return client, ctx
}
//
```



## Sending secure email over TLS

To send email safely with Go we should use TLS secure socket connections. We can do this like so:



```go
func Test_CanSendEmail_TLS(t *testing.T) {
	// create a context with your api key
	client, ctx := getMailSlurpClient(t)

	// create an inbox using the inbox controller
	opts := &mailslurp.CreateInboxOpts{
		InboxType: optional.NewString("SMTP_INBOX"),
	}

	// create two inboxes for testing
	inbox1, _, _ := client.InboxControllerApi.CreateInbox(ctx, opts)
	smtpAccess, _, _ := client.InboxControllerApi.GetImapSmtpAccess(ctx, &mailslurp.GetImapSmtpAccessOpts{
		InboxId: optional.NewInterface(inbox1.Id),
	})
	inbox2, _, _ := client.InboxControllerApi.CreateInbox(ctx, opts)

	// send email from inbox1 to inbox2
	auth := sasl.NewPlainClient("", smtpAccess.SmtpUsername, smtpAccess.SmtpPassword)

	// Connect to the server, authenticate, set the sender and recipient,
	// and send the email all in one step.
	to := []string{inbox2.EmailAddress}
	msg := strings.NewReader("To: " + inbox2.EmailAddress + "\r\n" +
		"Subject: Hello Gophers!\r\n" +
		"\r\n" +
		"This is the email body.\r\n")
	// not TLS mailslurp uses a different host
	err := smtp.SendMail("mailslurp.mx:587", auth, inbox1.EmailAddress, to, msg)
	if err != nil {
		log.Fatal(err)
		assert.NoError(t, err, "Expect smtp send to work")
	}

	// fetch the email for inbox2
	waitOpts := &mailslurp.WaitForLatestEmailOpts{
		InboxId:    optional.NewInterface(inbox2.Id),
		Timeout:    optional.NewInt64(30000),
		UnreadOnly: optional.NewBool(true),
	}
	email, _, err := client.WaitForControllerApi.WaitForLatestEmail(ctx, waitOpts)
	assert.NoError(t, err)
	assert.Contains(t, *email.Subject, "Hello Gophers")
	assert.Contains(t, *email.Body, "This is the email body")
}

//
```



Use implicit TLS when the server expects encryption from the first byte, and use STARTTLS when the server expects a plaintext SMTP greeting followed by an upgrade. Mixing the mode and port is one of the fastest ways to create confusing Go SMTP failures.

## How to send when insecure?

If necessary we can also send emails with TLS using an insecure connection:



```go
func Test_CanSendEmail_Insecure(t *testing.T) {
	// create a context with your api key
	client, ctx := getMailSlurpClient(t)

	// create an inbox using the inbox controller
	opts := &mailslurp.CreateInboxOpts{
		InboxType: optional.NewString("SMTP_INBOX"),
	}

	// create two inboxes for testing
	inbox1, _, _ := client.InboxControllerApi.CreateInbox(ctx, opts)
	smtpAccess, _, _ := client.InboxControllerApi.GetImapSmtpAccess(ctx, &mailslurp.GetImapSmtpAccessOpts{
		InboxId: optional.NewInterface(inbox1.Id),
	})
	inbox2, _, _ := client.InboxControllerApi.CreateInbox(ctx, opts)

	// create a plain auth client with smtp access details
	auth := sasl.NewPlainClient("", smtpAccess.SmtpUsername, smtpAccess.SmtpPassword)

	// dial connection to the smtp server
	c, err := smtp.Dial("mx.mailslurp.com:2525")
	assert.NoError(t, err, "Expect client dial")
	defer c.Close()

	// issue hello smtp command
	log.Println("Say hello")
	err = c.Hello("test")
	assert.NoError(t, err, "Expect hello")

	// issue auth smtp command
	log.Println("Set auth")
	err = c.Auth(auth)
	assert.NoError(t, err, "Expect auth")

	// send the email
	log.Println("Send email")
	to := []string{inbox2.EmailAddress}
	msg := strings.NewReader("To: " + inbox2.EmailAddress + "\r\n" +
		"Subject: Hello Insecure Gophers!\r\n" +
		"\r\n" +
		"This is the email body.\r\n")
	err = c.SendMail(inbox1.EmailAddress, to, msg)
	assert.NoError(t, err, "Expect insecure smtp send to work")

	// fetch the email for inbox2
	log.Println("Wait for email to arrive")
	waitOpts := &mailslurp.WaitForLatestEmailOpts{
		InboxId:    optional.NewInterface(inbox2.Id),
		Timeout:    optional.NewInt64(30000),
		UnreadOnly: optional.NewBool(true),
	}
	email, _, err := client.WaitForControllerApi.WaitForLatestEmail(ctx, waitOpts)

	// assert email contents
	log.Println("Email received: " + *email.Subject)
	assert.NoError(t, err)
	assert.Contains(t, *email.Subject, "Hello Insecure Gophers")
	assert.Contains(t, *email.Body, "This is the email body")
}

//
```



## Go SMTP deployment checklist

Before promoting Go SMTP code into production:

- Confirm the host, port, auth mode, and TLS mode with the [SMTP tester](/tools/smtp-tester/).
- Validate SMTP auth and message rendering in [Email Sandbox](/product/email-sandbox/).
- Add CI coverage for signup, reset, and notification paths with [Email Integration Testing](/product/email-integration-testing/).
- Capture send outcomes and message lifecycle events using [Email Webhooks](/guides/email-webhooks/).
- Route retries, bounces, and suppression decisions through [Email Automation Routing](/product/email-automation-routing/).
- Confirm inbox placement and sender reputation with an [Email Deliverability Test](/testing/email-deliverability-test/).
