Product
Golang send email: Send transactional email with Go in minutes
If you’re building with Go and need reliable email for sign-ups, receipts, password resets, or alerts, this guide shows the fastest path.
You’ll set up a sender, send your first message with Mailjet’s Go SDK, and learn patterns for templates, attachments, bulk sending, webhooks, and SMTP.
This is an example-first, production-ready approach to email integration.
Back in 2009, Google released a language called Go, often referred to as “Golang”. Go was introduced as a robust server-side programming language. Since then, it has never failed to fulfill its promises with low-level programming patterns and powerful data structures.
Who this guide is for
This guide is designed for:
- Developers and DevOps engineers who want a simple, durable “send email in Go” solution
- Teams who care about deliverability, analytics, and scale without the complexity of SMTP management
Prerequisites
Before getting started, you’ll need:
- A Mailjet account with API and Secret keys
- A validated sender email/domain (SPF and DKIM recommended for optimal inbox placement)
- Go 1.20+ (the SDK supports Go 1.13+) and access to set environment variables
đź’ˇ Pro tip: Authenticate your domain via SPF/DKIM to improve inbox delivery rates. See the complete authentication guide for setup details and examples.
Quickstart: Send your first Go email (API v3.1)
Let’s get you sending emails quickly using the official Mailjet Go SDK and the v3.1 Send API.
1. Install dependencies and set API keys
First, install the Mailjet Go SDK:
go get github.com/mailjet/mailjet-apiv3-go/v4
Then set your environment variables:
export MJ_APIKEY_PUBLIC=your_api_key
export MJ_APIKEY_PRIVATE=your_secret_key
export SENDER_EMAIL=your_verified_sender@yourdomain.com
2. Create your first email program
Create a main.go file with the following code:
package main
import (
"fmt"
"log"
"os"
"github.com/mailjet/mailjet-apiv3-go/v4"
)
func main() {
mj := mailjet.NewMailjetClient(os.Getenv("MJ_APIKEY_PUBLIC"), os.Getenv("MJ_APIKEY_PRIVATE"))
msgs := []mailjet.InfoMessagesV31{
{
From: &mailjet.RecipientV31{Email: os.Getenv("SENDER_EMAIL"), Name: "Go App"},
To: &mailjet.RecipientsV31{
{Email: "recipient@example.com", Name: "Recipient"},
},
Subject: "Hello from Go + Mailjet",
TextPart: "This is a plain-text fallback.",
HTMLPart: "<h3>Hello from Go + Mailjet</h3><p>It works.</p>",
},
}
messages := mailjet.MessagesV31{Info: msgs}
res, err := mj.SendMailV31(&messages)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Status: %+v\n", res)
}
Why this works
The Go SDK wraps Mailjet’s REST endpoints and provides a convenient SendMailV31 helper that accepts a Messages array. This approach gives you the full power of the Mailjet API with minimal code.
Send via SMTP in go (standard library)
If you prefer SMTP or already use an SMTP MTA, you can use Mailjet’s SMTP relay with your API credentials.
package main
import (
"net/smtp"
"os"
)
func main() {
host := "in-v3.mailjet.com"
auth := smtp.PlainAuth("", os.Getenv("MJ_APIKEY_PUBLIC"), os.Getenv("MJ_APIKEY_PRIVATE"), host)
to := []string{"recipient@example.com"}
from := "sender@yourdomain.com"
msg := []byte(
"To: recipient@example.com\r\n" +
"From: Go App <sender@yourdomain.com>\r\n" +
"Subject: SMTP from Go via Mailjet\r\n" +
"MIME-Version: 1.0\r\n" +
"Content-Type: text/html; charset=UTF-8\r\n\r\n" +
"<p>Hello via SMTP</p>",
)
// Port 587 (TLS) or 465 (SSL) are common choices
err := smtp.SendMail(host+":587", auth, from, to, msg)
if err != nil {
log.Fatal(err)
}
}
SMTP configuration notes
- SMTP host: in-v3.mailjet.com
- Common ports: 587 (TLS) or 465 (SSL)
- Port 25 considerations: May be blocked by your ISP or cloud provider
For more details, see the SMTP configuration guide.
Personalize with templates and variables
Design your email template once in the Mailjet dashboard, then pass a TemplateID and Variables from your Go code for dynamic content.
msgs := []mailjet.InfoMessagesV31{
{
From: &mailjet.RecipientV31{Email: os.Getenv("SENDER_EMAIL"), Name: "Go App"},
To: &mailjet.RecipientsV31{{Email: "recipient@example.com", Name: "Terry"}},
TemplateID: 123456, // replace with your template ID
TemplateLanguage: true, // enable Mailjet templating language
Variables: map[string]any{"first_name": "Terry", "plan": "Pro"},
Subject: "Welcome, {{var:first_name:\"there\"}}",
},
}
Template variable syntax
- API variables: Use {{var:first_name:”default_value”}} to insert variables from your API call
- Contact data: Use [[data:property:default]] for data stored in Mailjet contacts
For more template examples, see this Stack Overflow discussion.
Attach files and inline images
Send attachments and embed inline images in your emails. The total message size limit is 15 MB.
// Read and base64-encode your files first, then:
msgs := []mailjet.InfoMessagesV31{
{
From: &mailjet.RecipientV31{Email: os.Getenv("SENDER_EMAIL")},
To: &mailjet.RecipientsV31{{Email: "recipient@example.com"}},
Subject: "Invoice attached + inline logo",
HTMLPart: `<p>Thanks for your purchase!</p><img src="cid:logo1" alt="Company Logo">`,
Attachments: &[]mailjet.AttachmentV31{
{
ContentType: "application/pdf",
Filename: "invoice.pdf",
Base64Content: "<BASE64_STRING>", // your base64-encoded PDF
},
},
InlinedAttachments: &[]mailjet.InlinedAttachmentV31{
{
AttachmentV31: mailjet.AttachmentV31{
ContentType: "image/png",
Filename: "logo.png",
Base64Content: "<BASE64_PNG>", // your base64-encoded image
},
ContentID: "logo1", // referenced in HTML as cid:logo1
},
},
},
}
Attachment best practices
- Size limit: Keep total message size ≤ 15 MB
- Bulk emails: For better deliverability, consider linking to hosted files instead of attaching large files
- Inline images: Use the ContentID field to reference images in your HTML with cid: syntax
Send in bulk (multiple messages)
Send multiple personalized messages in a single API call. Each message is validated independently, so successful messages will be sent even if one fails.
msgs := []mailjet.InfoMessagesV31{
{
From: &mailjet.RecipientV31{Email: os.Getenv("SENDER_EMAIL")},
To: &mailjet.RecipientsV31{{Email: "user1@example.com"}},
Subject: "Welcome, User 1!",
HTMLPart: "<p>Hello User 1</p>",
},
{
From: &mailjet.RecipientV31{Email: os.Getenv("SENDER_EMAIL")},
To: &mailjet.RecipientsV31{{Email: "user2@example.com"}},
Subject: "Welcome, User 2!",
HTMLPart: "<p>Hello User 2</p>",
},
}
res, err := mj.SendMailV31(&mailjet.MessagesV31{Info: msgs})
if err != nil {
log.Fatal(err)
}
// Process results - each message gets its own status and ID
for i, result := range res.ResultsV31 {
fmt.Printf("Message %d: Status=%s, MessageID=%d\n", i, result.Status, result.To[0].MessageID)
}
The v3.1 Send API returns per-message status and IDs, making it easy to correlate results with your original messages. Use the MessageID to track delivery status later.
Track delivery, opens, and clicks (webhooks)
Configure webhooks to receive real-time notifications about email events like delivery, opens, clicks, bounces, and unsubscribes.
Setting up webhooks
- Register your webhook endpoint in the Mailjet dashboard under “Event Tracking”
- Or use the /v3/REST/eventcallbackurl API endpoint programmatically
Webhook handler example
package main
import (
"encoding/json"
"net/http"
"log"
)
type MailjetEvent struct {
Event string `json:"event"`
Time int64 `json:"time"`
MessageID int64 `json:"MessageID"`
Email string `json:"email"`
URL string `json:"url,omitempty"` // for click events
Error string `json:"error,omitempty"` // for bounce/error events
}
func webhookHandler(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
var events []MailjetEvent
if err := json.NewDecoder(r.Body).Decode(&events); err != nil {
http.Error(w, "bad request", http.StatusBadRequest)
return
}
// Process events (consider using a queue for production)
for _, event := range events {
switch event.Event {
case "sent":
log.Printf("Message %d sent to %s", event.MessageID, event.Email)
case "open":
log.Printf("Message %d opened by %s", event.MessageID, event.Email)
case "click":
log.Printf("Message %d clicked by %s (URL: %s)", event.MessageID, event.Email, event.URL)
case "bounce":
log.Printf("Message %d bounced for %s: %s", event.MessageID, event.Email, event.Error)
}
}
// Always return 200 to acknowledge receipt
w.WriteHeader(http.StatusOK)
}
func main() {
http.HandleFunc("/mj/events", webhookHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
Webhook best practices
- Return HTTP 200 quickly to acknowledge receipt
- Process events asynchronously to avoid timeouts
- Handle retries: Mailjet retries non-200 responses for up to 24 hours
For complete event documentation, see the Event Tracking API guide.
Deliverability essentials
Here are three deliverability essentials to keep in mind.
1. Domain authentication
Set up SPF and DKIM authentication for your sending domain to improve inbox placement and reduce the risk of spoofing:
- SPF Record: Authorizes Mailjet to send emails on behalf of your domain
- DKIM Signing: Adds cryptographic signatures to verify email authenticity
Follow the complete domain authentication guide for step-by-step setup instructions.
2. Regional configuration
If your Mailjet account is on the US infrastructure, configure the SDK to use the US API endpoint:
// For US region accounts
mj := mailjet.NewMailjetClient(apiKey, secretKey)
mj.SetAPIBase("https://api.us.mailjet.com/v3.1/")
3. SMTP configuration
- Default region: in-v3.mailjet.com
- Recommended ports: 587 (TLS) or 465 (SSL)
Firewall considerations: Test your network’s port access; many providers block port 25
Troubleshooting common issues
Here are the most common issues and how to troubleshoot them.
- Check your API keys are correctly set in environment variables
- Verify the API region: Use api.us.mailjet.com for US accounts, api.mailjet.com for EU accounts
Content validation errors (400 bad request)
- “At least HTMLPart, TextPart or TemplateID must be provided”: Ensure you include email content or specify a template ID
- Missing required fields: Verify From, To, and Subject are properly set
Message size errors (413 payload too large)
- Check total message size: Keep attachments and content under 15 MB total
- Consider alternatives: For large files, use hosted links instead of attachments
Webhook issues
- Events not being captured: Ensure your webhook endpoint returns HTTP 200 status
- Processing timeouts: Handle webhook processing asynchronously to avoid blocking Mailjet’s retry mechanism
Frequently asked questions
What’s the quickest way to send an email in Go?
Use the official SDK with SendMailV31 as shown in the Quickstart section. It’s a single function call once your API keys are configured.
Can I send emails using SMTP instead of the API?
Yes! Point Go’s net/smtp package at in-v3.mailjet.com using your API key and secret as credentials, with port 587 or 465.
How do I personalize email templates?
Set TemplateID, enable TemplateLanguage: true, and pass your data in the Variables field. Use {{var:name:”default”}} syntax in your templates.
Where can I view delivery and engagement metrics?
Use webhooks for real-time event data, or check the Mailjet dashboard and REST API statistics endpoints for aggregated metrics.
Next steps
Now that you have email sending working in your Go application, consider these enhancements:
- Secure your sender reputation: Authenticate your domain with SPF and DKIM records
- Add event tracking: Implement webhooks to power analytics and automated workflows
- Explore advanced features: Check out URLTags and SandboxMode for testing in the Send API v3.1 documentation
- Browse more examples: Visit the Go SDK repository for additional patterns and use cases
With this foundation, you’re ready to build robust, scalable email functionality into your Go applications. The combination of Mailjet’s reliable infrastructure and Go’s performance makes for a powerful email solution.