A production-ready Go client library for the Mailpit API, providing 100% coverage of all Mailpit API endpoints. Mailpit is a popular email testing tool with a REST API for managing emails, messages, tags, and server operations.
- 🚀 Production-ready with comprehensive error handling and retry logic
- 📡 100% API coverage - All Mailpit endpoints implemented and tested
- 🔄 Context support for cancellation and timeouts
- 🔒 TLS/HTTPS support with mkcert integration for testing
- ⚡ High performance with connection pooling and optimizations
- 🧪 Comprehensive testing with unit tests and E2E testing via testcontainers
- 🔧 Thread-safe - Safe for concurrent use across goroutines
- 📚 Well-documented with extensive examples and godoc comments
- Go 1.25.0 or later
- Mailpit server (for testing/production use)
go get github.com/CodeLieutenant/mailpitclientpackage main
import (
"context""fmt""log"
mailpit "github.com/CodeLieutenant/mailpitclient"
)
funcmain() {
// Create client with default configuration (localhost:8025)client, err:=mailpit.NewClient(nil)
iferr!=nil {
log.Fatal(err)
}
deferclient.Close()
ctx:=context.Background()
// Check server healthiferr:=client.HealthCheck(ctx); err!=nil {
log.Fatal("Mailpit server not accessible:", err)
}
// Get server informationinfo, err:=client.GetServerInfo(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Connected to Mailpit v%s\n", info.Version)
// List all messagesmessages, err:=client.ListMessages(ctx, nil)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d messages\n", messages.Total)
}config:=&mailpit.Config{
BaseURL: "https://mailpit.example.com",
Timeout: 30*time.Second,
MaxRetries: 3,
RetryDelay: 1*time.Second,
APIKey: "your-api-key", // For authenticated instances
}
client, err:=mailpit.NewClient(config)
iferr!=nil {
log.Fatal(err)
}
deferclient.Close()opts:=&mailpit.ListOptions{
Start: 0,
Limit: 10,
}
messages, err:=client.ListMessages(ctx, opts)
iferr!=nil {
log.Fatal(err)
}
for_, msg:=rangemessages.Messages {
fmt.Printf("ID: %s, Subject: %s, From: %s\n",
msg.ID, msg.Subject, msg.From.Address)
}message, err:=client.GetMessage(ctx, "message-id")
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Subject: %s\n", message.Subject)
fmt.Printf("From: %s <%s>\n", message.From.Name, message.From.Address)
fmt.Printf("Date: %s\n", message.Date.Format(time.RFC3339))
fmt.Printf("HTML Body: %s\n", message.HTML)
fmt.Printf("Text Body: %s\n", message.Text)// Search by sender emailresults, err:=client.SearchMessages(ctx, "from:test@example.com", nil)
iferr!=nil {
log.Fatal(err)
}
// Search with additional optionssearchOpts:=&mailpit.SearchOptions{
Start: 0,
Limit: 20,
}
results, err=client.SearchMessages(ctx, "subject:important", searchOpts)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Found %d matching messages\n", results.Total)// HTML validationhtmlCheck, err:=client.GetMessageHTMLCheck(ctx, "message-id")
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("HTML errors: %d, warnings: %d\n",
len(htmlCheck.Errors), len(htmlCheck.Warnings))
// Link validationlinkCheck, err:=client.GetMessageLinkCheck(ctx, "message-id")
iferr!=nil {
log.Fatal(err)
}
for_, link:=rangelinkCheck.Links {
fmt.Printf("URL: %s, Status: %d, Valid: %v\n",
link.URL, link.Status, link.Valid)
}
// SpamAssassin analysissaCheck, err:=client.GetMessageSpamAssassinCheck(ctx, "message-id")
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Spam score: %.2f, Required: %.2f\n",
saCheck.Score, saCheck.RequiredScore)message:=&mailpit.SendMessageRequest{
From: mailpit.Address{
Address: "sender@example.com",
Name: "Test Sender",
},
To: []mailpit.Address{
{Address: "recipient@example.com", Name: "Test Recipient"},
},
Cc: []mailpit.Address{
{Address: "cc@example.com", Name: "CC Recipient"},
},
Subject: "Test Message from Go Client",
HTML: "<h1>Hello World</h1><p>This is a <strong>test</strong> message.</p>",
Text: "Hello World\n\nThis is a test message.",
Tags: []string{"test", "automated", "go-client"},
}
result, err:=client.SendMessage(ctx, message)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Message sent with ID: %s\n", result.ID)// Get all available tagstags, err:=client.GetTags(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Available tags: %v\n", tags)
// Set global tagsnewTags:= []string{"important", "test", "automated", "production"}
updatedTags, err:=client.SetTags(ctx, newTags)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Updated global tags: %v\n", updatedTags)
// Tag specific messagesmessageIDs:= []string{"msg-1", "msg-2", "msg-3"}
err=client.SetMessageTags(ctx, "urgent", messageIDs)
iferr!=nil {
log.Fatal(err)
}
fmt.Println("Messages tagged as 'urgent'")
// Delete a tagerr=client.DeleteTag(ctx, "old-tag")
iferr!=nil {
log.Fatal(err)
}// Get comprehensive server informationinfo, err:=client.GetServerInfo(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Mailpit Version: %s\n", info.Version)
fmt.Printf("Database: %s\n", info.Database)
// Get server statisticsstats, err:=client.GetStats(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Total messages: %d\n", stats.Total)
fmt.Printf("Unread messages: %d\n", stats.Unread)
// Get Web UI configurationwebConfig, err:=client.GetWebUIConfig(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Read-only mode: %v\n", webConfig.ReadOnly)
fmt.Printf("SMTP server enabled: %v\n", webConfig.SMTPEnabled)releaseReq:=&mailpit.ReleaseMessageRequest{
To: []string{"recipient@production.com"},
Host: "smtp.gmail.com",
Port: 587,
Auth: &mailpit.SMTPAuth{
Username: "your-email@gmail.com",
Password: "app-password",
AuthType: "PLAIN",
},
TLS: true,
}
err:=client.ReleaseMessage(ctx, "message-id", releaseReq)
iferr!=nil {
log.Fatal(err)
}
fmt.Println("Message released via SMTP")// Get current chaos configurationchaosConfig, err:=client.GetChaosConfig(ctx)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Chaos testing enabled: %v\n", chaosConfig.Enabled)
// Configure chaos triggerstriggers:=&mailpit.ChaosTriggers{
AcceptConnections: 0.9, // 90% success rateRejectSenders: 0.1, // 10% sender rejectionDelayConnections: 0.2, // 20% connection delayDelayDuration: 5, // 5 second delays
}
updated, err:=client.SetChaosConfig(ctx, triggers)
iferr!=nil {
log.Fatal(err)
}
fmt.Printf("Chaos triggers updated: %+v\n", updated)message, err:=client.GetMessage(ctx, "invalid-id")
iferr!=nil {
varmailpitErr*mailpit.Erroriferrors.As(err, &mailpitErr) {
switchmailpitErr.Type {
casemailpit.ErrorTypeAPI:
ifmailpitErr.StatusCode==404 {
fmt.Println("Message not found")
} else {
fmt.Printf("API error %d: %s\n", mailpitErr.StatusCode, mailpitErr.Message)
}
casemailpit.ErrorTypeNetwork:
fmt.Println("Network error:", mailpitErr.Message)
casemailpit.ErrorTypeValidation:
fmt.Println("Validation error:", mailpitErr.Message)
casemailpit.ErrorTypeTimeout:
fmt.Println("Request timed out:", mailpitErr.Message)
}
} else {
fmt.Println("Unknown error:", err)
}
}This client is designed for seamless integration with testcontainers for comprehensive testing:
funcTestWithMailpit(t*testing.T) {
ctx:=context.Background()
// Start Mailpit containercontainer, err:=testcontainers.GenericContainer(ctx,
testcontainers.GenericContainerRequest{
ContainerRequest: testcontainers.ContainerRequest{
Image: "axllent/mailpit:latest",
ExposedPorts: []string{"8025/tcp", "1025/tcp"},
WaitingFor: wait.ForHTTP("/api/v1/info").WithPort("8025/tcp"),
Env: map[string]string{
"MP_SMTP_AUTH_ACCEPT_ANY": "1",
"MP_SMTP_AUTH_ALLOW_INSECURE": "1",
},
},
Started: true,
})
require.NoError(t, err)
defercontainer.Terminate(ctx)
// Get container connection detailshost, err:=container.Host(ctx)
require.NoError(t, err)
httpPort, err:=container.MappedPort(ctx, "8025")
require.NoError(t, err)
smtpPort, err:=container.MappedPort(ctx, "1025")
require.NoError(t, err)
// Create API clientclient, err:=mailpit.NewClient(&mailpit.Config{
BaseURL: fmt.Sprintf("http://%s:%s", host, httpPort.Port()),
Timeout: 10*time.Second,
})
require.NoError(t, err)
deferclient.Close()
// Send test email via SMTPsmtpClient, err:=smtp.Dial(fmt.Sprintf("%s:%s", host, smtpPort.Port()))
require.NoError(t, err)
defersmtpClient.Close()
err=smtpClient.Mail("test@example.com")
require.NoError(t, err)
err=smtpClient.Rcpt("recipient@example.com")
require.NoError(t, err)
writer, err:=smtpClient.Data()
require.NoError(t, err)
_, err=writer.Write([]byte(`Subject: Test EmailFrom: test@example.comTo: recipient@example.comThis is a test email body.`))
require.NoError(t, err)
require.NoError(t, writer.Close())
// Wait for message processingtime.Sleep(2*time.Second)
// Verify message receivedmessages, err:=client.ListMessages(ctx, nil)
require.NoError(t, err)
require.Equal(t, 1, messages.Total)
message:=messages.Messages[0]
assert.Equal(t, "Test Email", message.Subject)
assert.Equal(t, "test@example.com", message.From.Address)
}This client provides 100% coverage of the Mailpit API endpoints. For detailed endpoint mapping and implementation status, see our API Coverage Documentation.
We maintain automated testing to ensure complete API coverage:
# Run full API coverage validation
make test-api-coverage
# Run fast offline coverage test
make test-api-coverage-offline- Go 1.25.0+
- Docker (for testing)
- Make
- golangci-lint (for linting)
# Install dependencies
go mod download
# Run tests
make test# Run linting
make check
# Auto-fix lint issues
make fix
# Run field alignment optimization
make fieldalign
# Security scanning
make security
# Generate TLS certificates for testing
make mkcert-generate HOSTS='localhost 127.0.0.1 ::1'The project includes comprehensive testing:
- Unit Tests: Test individual functions and methods
- Integration Tests: End-to-end testing with real Mailpit instances
- API Coverage Tests: Automated verification of complete API coverage
# Run all tests
make test# Run with coverage
go test -race -coverprofile=coverage.out ./...
# View coverage report
go tool cover -html=coverage.out- API Coverage Documentation - Complete endpoint mapping
- GoDoc - Comprehensive API documentation
- Mailpit Documentation - Official Mailpit documentation
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Run tests and linting (
make check test) - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow the established patterns in the codebase
- Add tests for new functionality (unit + E2E)
- All tests must use
t.Parallel()for parallel execution - Maintain >90% code coverage
- Use
make checkto validate code quality - Update documentation as needed
This project is licensed under the Apache License 2.0. See the LICENSE file for details.
- Mailpit - The email testing tool this client connects to
- Testcontainers Go - Docker testing framework used in our test suite
If you find this project helpful, please consider giving it a star on GitHub!
For bugs, feature requests, or questions, please open an issue.
Built with ❤️ for the Go community