> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/pocketbase/pocketbase/llms.txt
> Use this file to discover all available pages before exploring further.

# Mailer

> Sending emails in PocketBase

PocketBase provides a mailer interface for sending emails using SMTP or Sendmail.

## Creating mail clients

### NewMailClient()

Create a mail client based on current app settings:

```go theme={null}
mailer := app.NewMailClient()
```

The mailer is automatically configured based on your app's email settings (configured via the Admin UI).

## Sending emails

### Basic email

```go theme={null}
import (
    "net/mail"
    "github.com/pocketbase/pocketbase/tools/mailer"
)

mailer := app.NewMailClient()

message := &mailer.Message{
    From: mail.Address{
        Name:    "My App",
        Address: "noreply@example.com",
    },
    To: []mail.Address{
        {Address: "user@example.com"},
    },
    Subject: "Welcome to My App",
    HTML:    "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
}

if err := mailer.Send(message); err != nil {
    log.Fatal(err)
}
```

### Email with text alternative

```go theme={null}
message := &mailer.Message{
    From: mail.Address{
        Address: "noreply@example.com",
    },
    To: []mail.Address{
        {Address: "user@example.com"},
    },
    Subject: "Welcome",
    HTML:    "<h1>Welcome!</h1><p>Thanks for signing up.</p>",
    Text:    "Welcome! Thanks for signing up.",
}

mailer.Send(message)
```

### Multiple recipients

```go theme={null}
message := &mailer.Message{
    From: mail.Address{
        Name:    "Newsletter",
        Address: "newsletter@example.com",
    },
    To: []mail.Address{
        {Address: "user1@example.com"},
        {Address: "user2@example.com"},
        {Address: "user3@example.com"},
    },
    Cc: []mail.Address{
        {Address: "manager@example.com"},
    },
    Bcc: []mail.Address{
        {Address: "admin@example.com"},
    },
    Subject: "Monthly Newsletter",
    HTML:    "<h1>Newsletter content</h1>",
}

mailer.Send(message)
```

## Email attachments

### Add file attachments

```go theme={null}
import "os"

file, err := os.Open("invoice.pdf")
if err != nil {
    log.Fatal(err)
}
defer file.Close()

message := &mailer.Message{
    From:    mail.Address{Address: "billing@example.com"},
    To:      []mail.Address{{Address: "customer@example.com"}},
    Subject: "Your Invoice",
    HTML:    "<p>Please find your invoice attached.</p>",
    Attachments: map[string]io.Reader{
        "invoice.pdf": file,
    },
}

mailer.Send(message)
```

### Multiple attachments

```go theme={null}
file1, _ := os.Open("document1.pdf")
defer file1.Close()

file2, _ := os.Open("document2.pdf")
defer file2.Close()

message := &mailer.Message{
    From:    mail.Address{Address: "noreply@example.com"},
    To:      []mail.Address{{Address: "user@example.com"}},
    Subject: "Your Documents",
    HTML:    "<p>Your requested documents are attached.</p>",
    Attachments: map[string]io.Reader{
        "document1.pdf": file1,
        "document2.pdf": file2,
    },
}

mailer.Send(message)
```

### Inline attachments (embedded images)

```go theme={null}
image, _ := os.Open("logo.png")
defer image.Close()

message := &mailer.Message{
    From:    mail.Address{Address: "noreply@example.com"},
    To:      []mail.Address{{Address: "user@example.com"}},
    Subject: "Welcome Email",
    HTML:    `<h1>Welcome!</h1><img src="cid:logo.png" alt="Logo">`,
    InlineAttachments: map[string]io.Reader{
        "logo.png": image,
    },
}

mailer.Send(message)
```

<Info>
  Inline attachments can be referenced in HTML using `cid:filename` where filename matches the key in InlineAttachments.
</Info>

## Custom headers

Add custom email headers:

```go theme={null}
message := &mailer.Message{
    From:    mail.Address{Address: "noreply@example.com"},
    To:      []mail.Address{{Address: "user@example.com"}},
    Subject: "Test Email",
    HTML:    "<p>Test content</p>",
    Headers: map[string]string{
        "X-Custom-Header": "custom-value",
        "Reply-To":        "support@example.com",
        "X-Priority":      "1",
    },
}

mailer.Send(message)
```

## Email templates

### Using Go templates

```go theme={null}
import "html/template"

type EmailData struct {
    Name  string
    Token string
    URL   string
}

tmpl := template.Must(template.New("welcome").Parse(`
<!DOCTYPE html>
<html>
<body>
    <h1>Welcome {{.Name}}!</h1>
    <p>Please verify your email by clicking the link below:</p>
    <a href="{{.URL}}/verify?token={{.Token}}">Verify Email</a>
</body>
</html>
`))

var buf bytes.Buffer
err := tmpl.Execute(&buf, EmailData{
    Name:  "John Doe",
    Token: "verification-token",
    URL:   "https://example.com",
})
if err != nil {
    log.Fatal(err)
}

message := &mailer.Message{
    From:    mail.Address{Address: "noreply@example.com"},
    To:      []mail.Address{{Address: "user@example.com"}},
    Subject: "Verify Your Email",
    HTML:    buf.String(),
}

mailer.Send(message)
```

### Using PocketBase templates

```go theme={null}
import "github.com/pocketbase/pocketbase/tools/template"

registry := template.NewRegistry()

html, err := registry.LoadFiles(
    "templates/email/welcome.html",
).Render(map[string]any{
    "name": "John Doe",
})
if err != nil {
    log.Fatal(err)
}

message := &mailer.Message{
    From:    mail.Address{Address: "noreply@example.com"},
    To:      []mail.Address{{Address: "user@example.com"}},
    Subject: "Welcome",
    HTML:    html,
}

mailer.Send(message)
```

## Intercepting emails

### OnMailerSend hook

Intercept all outgoing emails:

```go theme={null}
app.OnMailerSend().BindFunc(func(e *core.MailerEvent) error {
    log.Printf("Sending email to: %v", e.Message.To)
    log.Printf("Subject: %s", e.Message.Subject)
    
    // Continue sending
    return e.Next()
})
```

### Modify emails before sending

```go theme={null}
app.OnMailerSend().BindFunc(func(e *core.MailerEvent) error {
    // Add a custom header to all emails
    if e.Message.Headers == nil {
        e.Message.Headers = make(map[string]string)
    }
    e.Message.Headers["X-App-Version"] = "1.0.0"
    
    // Add BCC to all emails
    e.Message.Bcc = append(e.Message.Bcc, mail.Address{
        Address: "archive@example.com",
    })
    
    return e.Next()
})
```

### Block emails in development

```go theme={null}
app.OnMailerSend().BindFunc(func(e *core.MailerEvent) error {
    if e.App.IsDev() {
        log.Printf("[DEV] Email blocked: %s", e.Message.Subject)
        log.Printf("[DEV] To: %v", e.Message.To)
        return nil // Don't call e.Next() to prevent sending
    }
    return e.Next()
})
```

## Auth-related emails

### OnMailerRecordVerificationSend

Intercept verification emails:

```go theme={null}
app.OnMailerRecordVerificationSend("users").BindFunc(func(e *core.MailerRecordEvent) error {
    // Customize verification email
    e.Message.Subject = "Please verify your email address"
    
    // Access the record
    log.Printf("Sending verification to: %s", e.Record.Email())
    
    return e.Next()
})
```

### OnMailerRecordPasswordResetSend

Intercept password reset emails:

```go theme={null}
app.OnMailerRecordPasswordResetSend("users").BindFunc(func(e *core.MailerRecordEvent) error {
    e.Message.Subject = "Reset your password"
    return e.Next()
})
```

### OnMailerRecordEmailChangeSend

Intercept email change confirmation emails:

```go theme={null}
app.OnMailerRecordEmailChangeSend("users").BindFunc(func(e *core.MailerRecordEvent) error {
    log.Printf("Email change requested for user: %s", e.Record.Id)
    return e.Next()
})
```

### OnMailerRecordAuthAlertSend

Intercept auth alert emails (new login notifications):

```go theme={null}
app.OnMailerRecordAuthAlertSend("users").BindFunc(func(e *core.MailerRecordEvent) error {
    e.Message.Subject = "Security Alert: New Login Detected"
    return e.Next()
})
```

## Sending custom emails from hooks

### Welcome email on user registration

```go theme={null}
app.OnRecordAfterCreateSuccess("users").BindFunc(func(e *core.RecordEvent) error {
    mailer := e.App.NewMailClient()
    
    message := &mailer.Message{
        From: mail.Address{
            Name:    "My App",
            Address: "noreply@example.com",
        },
        To: []mail.Address{
            {Address: e.Record.Email()},
        },
        Subject: "Welcome to My App!",
        HTML: fmt.Sprintf(
            "<h1>Welcome %s!</h1><p>Thanks for joining.</p>",
            e.Record.GetString("name"),
        ),
    }
    
    if err := mailer.Send(message); err != nil {
        // Log error but don't fail the request
        e.App.Logger().Error("Failed to send welcome email", "error", err)
    }
    
    return e.Next()
})
```

### Notification on record creation

```go theme={null}
app.OnRecordAfterCreateSuccess("posts").BindFunc(func(e *core.RecordEvent) error {
    // Get all users who should be notified
    users, err := e.App.FindRecordsByFilter(
        "users",
        "notifications = true",
        "",
        100,
        0,
    )
    if err != nil {
        return err
    }
    
    mailer := e.App.NewMailClient()
    
    for _, user := range users {
        message := &mailer.Message{
            From:    mail.Address{Address: "noreply@example.com"},
            To:      []mail.Address{{Address: user.Email()}},
            Subject: "New Post Published",
            HTML: fmt.Sprintf(
                "<p>A new post '%s' has been published.</p>",
                e.Record.GetString("title"),
            ),
        }
        
        if err := mailer.Send(message); err != nil {
            e.App.Logger().Error("Failed to send notification", "error", err)
        }
    }
    
    return e.Next()
})
```

## HTML to text conversion

PocketBase can automatically convert HTML to plain text:

```go theme={null}
import "github.com/pocketbase/pocketbase/tools/mailer"

html := "<h1>Hello</h1><p>This is a <strong>test</strong></p>"
text := mailer.StripHTML(html)
// Result: "Hello\nThis is a test"

message := &mailer.Message{
    From:    mail.Address{Address: "noreply@example.com"},
    To:      []mail.Address{{Address: "user@example.com"}},
    Subject: "Test",
    HTML:    html,
    Text:    text, // Fallback for email clients that don't support HTML
}
```

## Message type signature

```go theme={null}
type Message struct {
    From              mail.Address         // Sender address
    To                []mail.Address       // Recipient addresses
    Bcc               []mail.Address       // Blind carbon copy addresses
    Cc                []mail.Address       // Carbon copy addresses
    Subject           string               // Email subject
    HTML              string               // HTML content
    Text              string               // Plain text content
    Headers           map[string]string    // Custom headers
    Attachments       map[string]io.Reader // File attachments
    InlineAttachments map[string]io.Reader // Inline/embedded attachments
}
```

## Best practices

### Always provide text alternative

```go theme={null}
message := &mailer.Message{
    // ... other fields
    HTML: htmlContent,
    Text: mailer.StripHTML(htmlContent),
}
```

### Handle errors gracefully

```go theme={null}
if err := mailer.Send(message); err != nil {
    // Log error but don't fail the request
    app.Logger().Error("Failed to send email", "error", err)
    // Optionally queue for retry
}
```

### Use templates for consistency

```go theme={null}
// Define reusable email templates
type EmailTemplates struct {
    welcome       *template.Template
    verification  *template.Template
    passwordReset *template.Template
}

// Load once at startup
templates := &EmailTemplates{
    welcome: template.Must(template.ParseFiles("templates/welcome.html")),
    // ...
}
```

### Respect email preferences

```go theme={null}
user, _ := app.FindRecordById("users", userId)

if user.GetBool("email_notifications") {
    mailer.Send(message)
}
```
