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).

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

go run .

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.