Email API Go Client

Simple, powerful email sending API with support for attachments and custom headers.

Quick Start

package main

import (
    "bytes"
    "encoding/json"
    "net/http"
)

func main() {
    data := map[string]interface{}{
        "from":    "[email protected]",
        "to":      "[email protected]",
        "subject": "Hello World",
        "html":    "<h1>Welcome!</h1>",
    }

    jsonData, err := json.Marshal(data)
    if err != nil {
        panic(err)
    }

    req, err := http.NewRequest(
        "POST",
        "https://api.shoutbox.net/send",
        bytes.NewBuffer(jsonData),
    )
    if err != nil {
        panic(err)
    }

    req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()
}

Package Setup

Create a new package for the Shoutbox API:

// shoutbox/client.go
package shoutbox

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "time"
)

type Client struct {
    APIKey     string
    BaseURL    string
    HTTPClient *http.Client
}

func NewClient(apiKey string) *Client {
    return &Client{
        APIKey:  apiKey,
        BaseURL: "https://api.shoutbox.net",
        HTTPClient: &http.Client{
            Timeout: time.Second * 10,
        },
    }
}

// EmailRequest represents the structure of an email request
type EmailRequest struct {
    From        string            `json:"from"`
    To          string            `json:"to"`
    Subject     string            `json:"subject"`
    HTML        string            `json:"html"`
    Name        string            `json:"name,omitempty"`
    ReplyTo     string            `json:"reply_to,omitempty"`
    Attachments []Attachment      `json:"attachments,omitempty"`
    Headers     map[string]string `json:"headers,omitempty"`
}

type Attachment struct {
    Filename    string `json:"filename"`
    Content     []byte `json:"content"`
    ContentType string `json:"content_type"`
}

func (c *Client) SendEmail(req *EmailRequest) error {
    jsonData, err := json.Marshal(req)
    if err != nil {
        return fmt.Errorf("error marshaling request: %w", err)
    }

    request, err := http.NewRequest(
        "POST",
        fmt.Sprintf("%s/send", c.BaseURL),
        bytes.NewBuffer(jsonData),
    )
    if err != nil {
        return fmt.Errorf("error creating request: %w", err)
    }

    request.Header.Set("Authorization", "Bearer "+c.APIKey)
    request.Header.Set("Content-Type", "application/json")

    resp, err := c.HTTPClient.Do(request)
    if err != nil {
        return fmt.Errorf("error sending request: %w", err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
    }

    return nil
}

Basic Request Structure

FieldTypeRequiredDescription
fromstringYesSender email address
tostringYesRecipient email address(es)
subjectstringYesEmail subject line
htmlstringYesHTML content of the email
namestringNoSender name
reply_tostringNoReply-to email address

Recipients

Multiple Recipients

client := shoutbox.NewClient(os.Getenv("SHOUTBOX_API_KEY"))

req := &shoutbox.EmailRequest{
    From:    "[email protected]",
    To:      "[email protected],[email protected]",
    Subject: "Team Update",
    HTML:    "<h1>Important Announcement</h1>",
}

err := client.SendEmail(req)
if err != nil {
    log.Fatal(err)
}

Named Recipients

req := &shoutbox.EmailRequest{
    From:    "[email protected]",
    To:      "John Doe <[email protected]>,Jane Smith <[email protected]>",
    Subject: "Team Meeting",
    HTML:    "<h1>Meeting Invitation</h1>",
}

err := client.SendEmail(req)
if err != nil {
    log.Fatal(err)
}

Reply-To Address

req := &shoutbox.EmailRequest{
    From:    "[email protected]",
    ReplyTo: "Support Team <[email protected]>",
    To:      "[email protected]",
    Subject: "Support Ticket Update",
    HTML:    "<h1>Your ticket has been updated</h1>",
}

err := client.SendEmail(req)
if err != nil {
    log.Fatal(err)
}

Attachments

Helper Functions for Attachments

// shoutbox/helpers.go
package shoutbox

import (
    "fmt"
    "io"
    "mime"
    "os"
    "path/filepath"
)

func NewAttachmentFromFile(filePath string) (Attachment, error) {
    content, err := os.ReadFile(filePath)
    if err != nil {
        return Attachment{}, fmt.Errorf("error reading file: %w", err)
    }

    // Detect content type
    contentType := mime.TypeByExtension(filepath.Ext(filePath))
    if contentType == "" {
        contentType = "application/octet-stream"
    }

    return Attachment{
        Filename:    filepath.Base(filePath),
        Content:     content,
        ContentType: contentType,
    }, nil
}

func NewAttachmentFromReader(reader io.Reader, filename string) (Attachment, error) {
    content, err := io.ReadAll(reader)
    if err != nil {
        return Attachment{}, fmt.Errorf("error reading content: %w", err)
    }

    contentType := mime.TypeByExtension(filepath.Ext(filename))
    if contentType == "" {
        contentType = "application/octet-stream"
    }

    return Attachment{
        Filename:    filename,
        Content:     content,
        ContentType: contentType,
    }, nil
}

Complete Example with Attachments

// Add PDF attachment
pdfAttachment, err := shoutbox.NewAttachmentFromFile("january_report.pdf")
if err != nil {
    log.Fatal(err)
}

// Add Excel attachment
xlsAttachment, err := shoutbox.NewAttachmentFromFile("data.xlsx")
if err != nil {
    log.Fatal(err)
}

req := &shoutbox.EmailRequest{
    From:        "[email protected]",
    Name:        "Reports Team",
    To:          "John Smith <[email protected]>",
    Subject:     "Monthly Report - January 2024",
    HTML:        "<h1>Monthly Report</h1><p>Please find your report attached.</p>",
    Attachments: []shoutbox.Attachment{pdfAttachment, xlsAttachment},
}

err = client.SendEmail(req)
if err != nil {
    log.Fatal(err)
}

Custom Headers

Complete Example with Headers

req := &shoutbox.EmailRequest{
    From:    "[email protected]",
    Name:    "Newsletter Team",
    To:      "Subscriber <[email protected]>",
    ReplyTo: "Newsletter Support <[email protected]>",
    Subject: "Your Weekly Newsletter",
    HTML:    "<h1>This Week's Updates</h1><p>Latest news and updates...</p>",
    Headers: map[string]string{
        "List-Unsubscribe":          "<https://yourdomain.com/unsubscribe>",
        "List-Unsubscribe-Post":     "List-Unsubscribe=One-Click",
        "X-Campaign-ID":             "newsletter_2024_01",
        "X-Mailer":                  "ShoutboxAPI/1.0",
        "Precedence":                "bulk",
        "X-Auto-Response-Suppress":   "OOF, AutoReply",
    },
}

err := client.SendEmail(req)
if err != nil {
    log.Fatal(err)
}

Context Support

Add context support to handle timeouts and cancellation:

// shoutbox/client.go
func (c *Client) SendEmailWithContext(ctx context.Context, req *EmailRequest) error {
    jsonData, err := json.Marshal(req)
    if err != nil {
        return fmt.Errorf("error marshaling request: %w", err)
    }

    request, err := http.NewRequestWithContext(
        ctx,
        "POST",
        fmt.Sprintf("%s/send", c.BaseURL),
        bytes.NewBuffer(jsonData),
    )
    if err != nil {
        return fmt.Errorf("error creating request: %w", err)
    }

    request.Header.Set("Authorization", "Bearer "+c.apiKey)
    request.Header.Set("Content-Type", "application/json")

    resp, err := c.HTTPClient.Do(request)
    if err != nil {
        return fmt.Errorf("error sending request: %w", err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusOK {
        return fmt.Errorf("unexpected status code: %d", resp.StatusCode)
    }

    return nil
}

Usage with context:

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

err := client.SendEmailWithContext(ctx, req)
if err != nil {
    log.Fatal(err)
}

Security Best Practices

API Key Management

Use environment variables for API keys:

apiKey := os.Getenv("SHOUTBOX_API_KEY")
if apiKey == "" {
    log.Fatal("SHOUTBOX_API_KEY environment variable is not set")
}

Error Handling

Implement proper error handling:

if err := client.SendEmail(req); err != nil {
    switch {
    case errors.Is(err, context.DeadlineExceeded):
        log.Printf("request timed out: %v", err)
    default:
        log.Printf("failed to send email: %v", err)
    }
    return
}

Email Validation

Use the provided validation functions:

if err := shoutbox.ValidateEmail(email); err != nil {
    log.Printf("invalid email address: %v", err)
    return
}

if err := shoutbox.ValidateEmailList(emails); err != nil {
    log.Printf("invalid email addresses: %v", err)
    return
}

Rate Limits

The following rate limits apply:

  • 60 requests per minute per API key
  • Maximum attachment size: 10MB
  • Maximum recipients per email: 50

Please contact support if you need higher limits for your use case.

Support

For additional support or questions, please contact our support team.