Mailgun with Go

Go library for interacting with the Mailgun API.
Installation
If you are using Go Modules make sure you
include the /v4
at the end of your import paths
$ go get github.com/mailgun/mailgun-go/v4
Usage
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/mailgun/mailgun-go/v4"
)
var yourDomain = "your-domain-name"
var privateAPIKey = "your-private-key"
func main() {
mg := mailgun.NewMailgun(yourDomain, privateAPIKey)
sender := "sender@example.com"
subject := "Fancy subject!"
body := "Hello from Mailgun Go!"
recipient := "recipient@example.com"
message := mailgun.NewMessage(sender, subject, body, recipient)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
resp, id, err := mg.Send(ctx, message)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %s Resp: %s\n", id, resp)
}
Get Events
package main
import (
"context"
"fmt"
"time"
"github.com/mailgun/mailgun-go/v4"
"github.com/mailgun/mailgun-go/v4/events"
)
func main() {
mg := mailgun.NewMailgun("your-domain.com", "your-private-key")
it := mg.ListEvents(&mailgun.ListEventOptions{Limit: 100})
var page []mailgun.Event
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
for it.Next(ctx, &page) {
for _, e := range page {
fmt.Printf("Event: '%s' TimeStamp: '%s'\n", e.GetName(), e.GetTimestamp())
switch event := e.(type) {
case *events.Accepted:
fmt.Printf("Accepted: auth: %t\n", event.Flags.IsAuthenticated)
case *events.Delivered:
fmt.Printf("Delivered transport: %s\n", event.Envelope.Transport)
case *events.Failed:
fmt.Printf("Failed reason: %s\n", event.Reason)
case *events.Clicked:
fmt.Printf("Clicked GeoLocation: %s\n", event.GeoLocation.Country)
case *events.Opened:
fmt.Printf("Opened GeoLocation: %s\n", event.GeoLocation.Country)
case *events.Rejected:
fmt.Printf("Rejected reason: %s\n", event.Reject.Reason)
case *events.Stored:
fmt.Printf("Stored URL: %s\n", event.Storage.URL)
case *events.Unsubscribed:
fmt.Printf("Unsubscribed client OS: %s\n", event.ClientInfo.ClientOS)
}
}
}
}
Event Polling
The mailgun library has built-in support for polling the events api
package main
import (
"context"
"log"
"time"
"github.com/mailgun/mailgun-go/v4"
)
func main() {
mg := mailgun.NewMailgun("your-domain.com", "your-private-key")
begin := time.Now().Add(time.Second * -3)
it := mg.PollEvents(&mailgun.ListEventOptions{
Begin: begin,
PollInterval: time.Second * 30,
})
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
var page []mailgun.Event
for it.Poll(ctx, &page) {
for _, e := range page {
log.Printf("Got an event: %q (%q)", e.GetName(), e.GetID())
}
}
}
Email Validations
package main
import (
"context"
"fmt"
"time"
"github.com/mailgun/mailgun-go/v4"
)
var apiKey string = "your-api-key"
func main() {
v := mailgun.NewEmailValidator(apiKey)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
email, err := v.ValidateEmail(ctx, "recipient@example.com", false)
if err != nil {
panic(err)
}
fmt.Printf("Valid: %t\n", email.IsValid)
}
Webhook Handling
package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
"github.com/mailgun/mailgun-go/v4"
"github.com/mailgun/mailgun-go/v4/events"
)
func main() {
mg := mailgun.NewMailgun("your-domain.com", "private-api-key")
mg.SetWebhookSigningKey("webhook-signing-key")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var payload mailgun.WebhookPayload
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
fmt.Printf("decode JSON error: %s", err)
w.WriteHeader(http.StatusNotAcceptable)
return
}
verified, err := mg.VerifyWebhookSignature(payload.Signature)
if err != nil {
fmt.Printf("verify error: %s\n", err)
w.WriteHeader(http.StatusNotAcceptable)
return
}
if !verified {
w.WriteHeader(http.StatusNotAcceptable)
fmt.Printf("failed verification %+v\n", payload.Signature)
return
}
fmt.Printf("Verified Signature\n")
e, err := mailgun.ParseEvent(payload.EventData)
if err != nil {
fmt.Printf("parse event error: %s\n", err)
return
}
switch event := e.(type) {
case *events.Accepted:
fmt.Printf("Accepted: auth: %t\n", event.Flags.IsAuthenticated)
case *events.Delivered:
fmt.Printf("Delivered transport: %s\n", event.Envelope.Transport)
}
})
fmt.Println("Serve on :9090...")
if err := http.ListenAndServe(":9090", nil); err != nil {
fmt.Printf("serve error: %s\n", err)
os.Exit(1)
}
}
Sending HTML templates
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/mailgun/mailgun-go/v4"
)
var yourDomain = "your-domain-name"
var privateAPIKey = "your-private-key"
func main() {
mg := mailgun.NewMailgun(yourDomain, privateAPIKey)
sender := "sender@example.com"
subject := "HTML email!"
recipient := "recipient@example.com"
message := mailgun.NewMessage(sender, subject, "", recipient)
body := `
<html>
<body>
<h1>Sending HTML emails with Mailgun</h1>
<p style="color:blue; font-size:30px;">Hello world</p>
<p style="font-size:30px;">More examples can be found <a href="https://documentation.mailgun.com/en/latest/api-sending.html#examples">here</a></p>
</body>
</html>
`
message.SetHTML(body)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
resp, id, err := mg.Send(ctx, message)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %s Resp: %s\n", id, resp)
}
Using Templates
Templates enable you to create message templates on your Mailgun account and then populate the data variables at send-time. This allows you to have your layout and design managed on the server and handle the data on the client. The template variables are added as a JSON stringified X-Mailgun-Variables
header. For example, if you have a template to send a password reset link, you could do the following:
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/mailgun/mailgun-go/v4"
)
var yourDomain = "your-domain-name"
var privateAPIKey = "your-private-key"
func main() {
mg := mailgun.NewMailgun(yourDomain, privateAPIKey)
sender := "sender@example.com"
subject := "Fancy subject!"
body := ""
recipient := "recipient@example.com"
message := mailgun.NewMessage(sender, subject, body, recipient)
message.SetTemplate("passwordReset")
err := message.AddTemplateVariable("passwordResetLink", "some link to your site unique to your user")
if err != nil {
log.Fatal(err)
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
resp, id, err := mg.Send(ctx, message)
if err != nil {
log.Fatal(err)
}
fmt.Printf("ID: %s Resp: %s\n", id, resp)
}
The official mailgun documentation includes examples using this library. Go
here
and click on the "Go" button at the top of the page.
EU Region
European customers will need to change the default API Base to access your domains
mg := mailgun.NewMailgun("your-domain.com", "private-api-key")
mg.SetAPIBase(mailgun.APIBaseEU)
Testing
WARNING - running the tests will cost you money!
To run the tests various environment variables must be set. These are:
MG_DOMAIN
is the domain name - this is a value registered in the Mailgun admin interface.
MG_PUBLIC_API_KEY
is the Public Validation API key - you can get this value from the Mailgun security page
MG_API_KEY
is the Private API key - you can get this value from the Mailgun security page
MG_EMAIL_TO
is the email address used in various sending tests.
and finally
MG_SPEND_MONEY
if this value is set the part of the test that use the API to actually send email will be run - be aware this will count on your quota and this will cost you money.
The code is released under a 3-clause BSD license. See the LICENSE file for more information.