go-notificationgo-notification
Examples

Email via API (Mailgun / SendGrid)

A complete minimal program that sends email via an API-based driver.

This is the recommended production setup — API-based driver instead of SMTP, so it works on any cloud host (see SMTP Port Blocking).

Examples live in their own Go module

The runnable version of this program is in _examples/email-api of the repository. The _examples/ directory is a separate Go module, so the library itself keeps zero third-party dependencies. Run it from there: cd _examples && go run ./email-api.

Mailgun variant

main.go
package main

import (
    "context"
    "log"
    "os"
    "time"

    notification "github.com/gopackx/go-notification"
    "github.com/gopackx/go-notification/channel/mail"
    "github.com/gopackx/go-notification/channel/mail/mailgun"
)

type User struct {
    ID    string
    Name  string
    Email string
}

func (u User) GetID() string { return u.ID }

func (u User) RouteNotificationFor(channel string) string {
    if channel == "mail" {
        return u.Email
    }
    return ""
}

type PasswordReset struct {
    ResetURL string
}

func (n PasswordReset) Via(_ notification.Notifiable) []string { return []string{"mail"} }

func (n PasswordReset) ToMail(notifiable notification.Notifiable) *mail.Message {
    u := notifiable.(User)
    return mail.NewMessage().
        SetSubject("Reset your password").
        SetGreeting("Hi " + u.Name + ",").
        Line("We received a request to reset your password.").
        Action("Reset password", n.ResetURL).
        Line("This link expires in 30 minutes. If you didn't request this, ignore this email.")
}

func main() {
    notifier := notification.New(notification.Config{})

    notifier.RegisterChannel(mailgun.New(mailgun.Config{
        Domain: "mg.example.com",
        APIKey: os.Getenv("MAILGUN_API_KEY"),
        From:   mail.Address{Name: "Acme", Address: "no-reply@example.com"},
    }))

    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    user := User{ID: "1", Name: "Dani", Email: "dani@example.com"}
    if err := notifier.Send(ctx, user, PasswordReset{
        ResetURL: "https://app.example.com/reset?token=abc",
    }); err != nil {
        log.Fatal(err)
    }
    notifier.Close()
}

SendGrid variant

Swap the channel registration only — notification code is identical.

main.go
import (
    "github.com/gopackx/go-notification/channel/mail"
    "github.com/gopackx/go-notification/channel/mail/sendgrid"
)

notifier.RegisterChannel(sendgrid.New(sendgrid.Config{
    APIKey: os.Getenv("SENDGRID_API_KEY"),
    From:   mail.Address{Name: "Acme", Address: "no-reply@example.com"},
}))

Env setup

terminal.sh
# Mailgun
export MAILGUN_API_KEY=key-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# or SendGrid
export SENDGRID_API_KEY=SG.xxxxxxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

# from a clone of the repository
cd _examples && go run ./email-api

Deliverability checklist

If your mail lands in spam:

  1. Verify SPF, DKIM, and DMARC on your sending domain. Both Mailgun and SendGrid walk you through this in their dashboards — don't skip any record.
  2. Use a dedicated subdomain for transactional mail (e.g. mg.example.com or mail.example.com). Reputation on that subdomain is independent of your main domain's web traffic.
  3. Warm up a new sending domain — start with low volume for the first 2–4 weeks and ramp gradually.
  4. Handle bounces and complaints. When the provider reports a hard bounce, mark that address as undeliverable in your DB and stop sending to it.
  5. Avoid marketing-looking subject lines for transactional mail — words like "FREE", "GUARANTEED", all-caps, or lots of emojis can trip spam filters.

Testing locally without sending

Replace the driver with Mailtrap in sandbox mode:

main.go
import (
    "strconv"

    "github.com/gopackx/go-notification/channel/mail"
    "github.com/gopackx/go-notification/channel/mail/mailtrap"
)

if os.Getenv("APP_ENV") == "production" {
    notifier.RegisterChannel(mailgun.New(mailgun.Config{ /* ... */ }))
} else {
    inboxID, _ := strconv.Atoi(os.Getenv("MAILTRAP_INBOX_ID"))
    notifier.RegisterChannel(mailtrap.New(mailtrap.Config{
        Sandbox:  true,
        APIToken: os.Getenv("MAILTRAP_API_TOKEN"),
        InboxID:  inboxID,
        From:     mail.Address{Name: "Dev", Address: "dev@example.com"},
    }))
}

Mail in dev goes to a Mailtrap inbox; in prod, to real inboxes. No code change between environments.