unsent
unsent.dev
Get Started

Go

Official Unsent Go SDK for sending emails and managing contacts.

This guide shows how to install and use the official unsent Go SDK.

Installation

Install the SDK using go get to add it to your project's dependencies:

go get github.com/souravsspace/unsent-go/pkg/unsent

Initialize

To start using the SDK, you need to create a client instance. You'll need your API key, which you can find in your Unsent dashboard.

Import the package and create a new client using unsent.NewClient. You can pass the API key directly, or if you have the UNSENT_API_KEY environment variable set, you can pass an empty string.

package main

import (
    "log"
    "github.com/souravsspace/unsent-go/pkg/unsent"
)

func main() {
    // Initialize with API key
    client, err := unsent.NewClient("un_xxx")
    
    // Or use environment variable UNSENT_API_KEY
    // client, err := unsent.NewClient("")
    
    if err != nil {
        log.Fatal(err)
    }
}

Send an email

Now you're ready to send your first email. Use the client.Emails.Send method with a SendEmailJSONBody struct.

Here is a simple example of sending an HTML email:

package main

import (
    "fmt"
    "log"
    "github.com/souravsspace/unsent-go/pkg/unsent"
)

// Helper function for string pointers
func stringPtr(s string) *string {
    return &s
}

func main() {
    // Initialize the client
    client, err := unsent.NewClient("un_xxx")
    if err != nil {
        log.Fatal(err)
    }

    // Send the email
    email, err := client.Emails.Send(unsent.SendEmailJSONBody{
        To:      toEmail("user@example.com"),
        From:    "no-reply@yourdomain.com",
        Subject: stringPtr("Welcome"),
        Html:    stringPtr("<strong>Hello!</strong>"),
    })

    if err != nil {
        log.Printf("Error: %v", err)
    } else {
        fmt.Printf("Email sent! ID: %s\n", email.EmailID)
    }
}

// Helper to construct email recipient
func toEmail(email string) unsent.SendEmailJSONBody_To {
    var to unsent.SendEmailJSONBody_To
    json.Unmarshal([]byte(`"`+email+`"`), &to)
    return to
}

With attachments and scheduling

You can also send emails with attachments and schedule them for a later time.

  • Attachments: Provide base64-encoded content
  • Scheduling: Set the ScheduledAt field with a time.Time pointer
import "time"

// Schedule for 10 minutes from now
scheduledTime := time.Now().Add(10 * time.Minute)

email, err := client.Emails.Create(unsent.SendEmailJSONBody{
    To:      toEmail("user@example.com"),
    From:    "no-reply@yourdomain.com",
    Subject: stringPtr("Report"),
    Text:    stringPtr("See attached."),
    Attachments: &[]map[string]interface{}{
        {
            "filename": "report.txt",
            "content":  "SGVsbG8gd29ybGQ=", // Base64 encoded
        },
    },
    ScheduledAt: &scheduledTime,
})

Batch send

If you need to send multiple emails at once, use the client.Emails.Batch method.

batchEmails := unsent.SendBatchEmailsJSONBody{
    Emails: []unsent.SendBatchEmailsJSONBody_Emails_Item{
        {
            To:      toEmail("a@example.com"),
            From:    "no-reply@yourdomain.com",
            Subject: stringPtr("Email A"),
            Html:    stringPtr("<p>Content A</p>"),
        },
        {
            To:      toEmail("b@example.com"),
            From:    "no-reply@yourdomain.com",
            Subject: stringPtr("Email B"),
            Html:    stringPtr("<p>Content B</p>"),
        },
    },
}

response, err := client.Emails.Batch(batchEmails)
if err != nil {
    log.Printf("Error: %v", err)
} else {
    fmt.Printf("Sent %d emails\n", len(response.Emails))
}

Idempotent Retries

To safely retry requests, you can use unsent.WithIdempotencyKey. This ensures that duplicate requests with the same key won't result in duplicate emails.

// For single email
client.Emails.Send(email, unsent.WithIdempotencyKey("unique-key-123"))

// For batch emails
client.Emails.Batch(emails, unsent.WithIdempotencyKey("batch-unique-key-123"))

Retrieve and manage emails

You can retrieve the status of sent emails, update their scheduled time, or cancel them if they haven't been sent yet.

List emails

Retrieve a list of sent emails with pagination:

emails, err := client.Emails.List(unsent.ListEmailsParams{
    Page:  stringPtr("1"),
    Limit: stringPtr("50"),
})

if err != nil {
    log.Printf("Error: %v", err)
} else {
    for _, email := range *emails {
        fmt.Printf("Email ID: %s, Status: %s\n", email.ID, email.Status)
    }
}

Get an email

To check the status of a specific email, use client.Emails.Get with the email ID:

email, err := client.Emails.Get("email_123")
if err != nil {
    log.Printf("Error: %v", err)
} else {
    fmt.Printf("Email status: %s\n", email.Status)
}

Update schedule time

If you need to change when a scheduled email will be sent:

import "time"

newTime := time.Now().Add(1 * time.Hour)

response, err := client.Emails.Update("email_123", unsent.UpdateEmailJSONBody{
    ScheduledAt: &newTime,
})

Cancel a scheduled email

To stop a scheduled email from being sent:

response, err := client.Emails.Cancel("email_123")
if err != nil {
    log.Printf("Error: %v", err)
} else {
    fmt.Println("Email cancelled successfully")
}

Get bounced emails

Retrieve emails that bounced:

func float32Ptr(f float32) *float32 {
    return &f
}

bounces, err := client.Emails.GetBounces(unsent.GetBouncesParams{
    Page:  float32Ptr(1.0),
    Limit: float32Ptr(20.0),
})

Get spam complaints

Retrieve emails marked as spam:

complaints, err := client.Emails.GetComplaints(unsent.GetComplaintsParams{
    Page:  float32Ptr(1.0),
    Limit: float32Ptr(20.0),
})

Get unsubscribes

Retrieve unsubscribed contacts:

unsubscribes, err := client.Emails.GetUnsubscribes(unsent.GetUnsubscribesParams{
    Page:  float32Ptr(1.0),
    Limit: float32Ptr(20.0),
})

Contact Books

Manage contact books to organize your contacts.

List contact books

books, err := client.ContactBooks.List()
if err != nil {
    log.Printf("Error: %v", err)
} else {
    for _, book := range *books {
        fmt.Printf("Book: %s (ID: %s)\n", book.Name, book.ID)
    }
}

Create a contact book

book, err := client.ContactBooks.Create(unsent.CreateContactBookJSONBody{
    Name:  "My Contacts",
    Emoji: stringPtr("๐Ÿ“ง"),
})

Get contact book details

book, err := client.ContactBooks.Get("book_123")

Update a contact book

response, err := client.ContactBooks.Update("book_123", unsent.UpdateContactBookJSONBody{
    Name: stringPtr("Updated Name"),
})

Delete a contact book

response, err := client.ContactBooks.Delete("book_123")

Contacts

Manage your audience by creating and updating contacts. Contacts must belong to a contact book.

List contacts

Retrieve contacts from a contact book with pagination:

contacts, err := client.Contacts.List("book_123", unsent.GetContactsParams{
    Page:  float32Ptr(1.0),
    Limit: float32Ptr(50.0),
})

Create a contact

Add a new contact to a specific contact book:

contact, err := client.Contacts.Create("book_123", unsent.CreateContactJSONBody{
    Email:     "user@example.com",
    FirstName: stringPtr("Jane"),
    LastName:  stringPtr("Doe"),
    Metadata: &map[string]string{
        "plan": "pro",
    },
})

Get a contact

Retrieve details about a specific contact:

contact, err := client.Contacts.Get("book_123", "contact_456")

Update a contact

Update a contact's information:

response, err := client.Contacts.Update("book_123", "contact_456", unsent.UpdateContactJSONBody{
    FirstName: stringPtr("John"),
    Metadata: &map[string]string{
        "plan": "enterprise",
    },
})

Upsert a contact

Create a contact if they don't exist, or update them if they do:

contact, err := client.Contacts.Upsert("book_123", "contact_456", unsent.UpsertContactJSONBody{
    Email:     "user@example.com",
    FirstName: stringPtr("Jane"),
})

Delete a contact

Remove a contact from a book:

response, err := client.Contacts.Delete("book_123", "contact_456")

Campaigns

Campaigns allow you to send emails to an entire contact book.

List campaigns

campaigns, err := client.Campaigns.List()

Create a campaign

Create a new campaign by specifying the content and the target contact book ID:

campaign, err := client.Campaigns.Create(unsent.CreateCampaignJSONBody{
    Name:          "Welcome Series",
    Subject:       "Welcome!",
    Html:          stringPtr("<p>Thanks for joining us!</p>"),
    From:          "welcome@yourdomain.com",
    ContactBookId: "book_123",
})

Get campaign details

campaign, err := client.Campaigns.Get("campaign_123")

Schedule a campaign

Once created, you can schedule the campaign to be sent at a specific time:

response, err := client.Campaigns.Schedule("campaign_123", unsent.ScheduleCampaignJSONBody{
    ScheduledAt: stringPtr("2024-12-01T10:00:00Z"),
})

Pause and resume campaigns

You can pause a running or scheduled campaign and resume it later:

// Pause
pauseResp, err := client.Campaigns.Pause("campaign_123")

// Resume
resumeResp, err := client.Campaigns.Resume("campaign_123")

Domains

Manage the domains you use to send emails.

List domains

Retrieve a list of all your domains and their verification status:

domains, err := client.Domains.List()
if err != nil {
    log.Printf("Error: %v", err)
} else {
    for _, domain := range *domains {
        fmt.Printf("Domain: %s, Status: %s\n", domain.Domain, domain.Status)
    }
}

Create a domain

Add a new domain to your account:

domain, err := client.Domains.Create(unsent.CreateDomainJSONBody{
    Domain: "yourdomain.com",
})

Get domain details

domain, err := client.Domains.Get("domain_123")

Verify a domain

Trigger domain verification:

response, err := client.Domains.Verify("domain_123")

Delete a domain

Remove a domain from your account:

response, err := client.Domains.Delete("domain_123")

Analytics

Get insights into your email performance.

Get analytics overview

analytics, err := client.Analytics.Get()
if err != nil {
    log.Printf("Error: %v", err)
} else {
    fmt.Printf("Total: %d, Sent: %d, Delivered: %d, Opened: %d\n",
        analytics.Total, analytics.Sent, analytics.Delivered, analytics.Opened)
}

SMTP

You can also send emails via SMTP using Unsent's SMTP server.

import (
    "fmt"
    "net/smtp"
)

func sendViaSMTP(apiKey, from, to, subject, body string) error {
    // SMTP configuration
    smtpHost := "smtp.unsent.dev"
    smtpPort := "587"
    smtpUser := "apikey"

    // Authentication
    auth := smtp.PlainAuth("", smtpUser, apiKey, smtpHost)

    // Email content
    msg := []byte("To: " + to + "\r\n" +
        "From: " + from + "\r\n" +
        "Subject: " + subject + "\r\n" +
        "MIME-Version: 1.0\r\n" +
        "Content-Type: text/html; charset=UTF-8\r\n" +
        "\r\n" +
        body + "\r\n")

    // Send email
    addr := fmt.Sprintf("%s:%s", smtpHost, smtpPort)
    return smtp.SendMail(addr, auth, from, []string{to}, msg)
}

Error handling

The SDK supports different ways of handling errors.

Standard Error Handling (Default)

By default, SDK methods return an *APIError if the API request fails or returns a non-2xx status code:

import "github.com/souravsspace/unsent-go/pkg/unsent"

client, err := unsent.NewClient("un_xxx")
if err != nil {
    log.Fatal(err)
}

email, err := client.Emails.Get("email_123")
if err != nil {
    // err is *unsent.APIError
    fmt.Printf("API Error: %s - %s\n", err.Code, err.Message)
}

Disable Automatic Error Raising

If you prefer to handle HTTP errors manually without the SDK treating non-2xx responses as errors, you can use unsent.WithRaiseOnError(false):

client, err := unsent.NewClient("un_xxx", unsent.WithRaiseOnError(false))

Advanced Configuration

For advanced use cases, such as adding custom timeouts, proxies, or changing the base URL, you can provide options to NewClient.

Custom HTTP Client

import (
    "net/http"
    "time"
)

// Create a custom HTTP client with a timeout
httpClient := &http.Client{
    Timeout: 30 * time.Second,
}

// Initialize the SDK with the custom client
client, err := unsent.NewClient("un_xxx", unsent.WithHTTPClient(httpClient))

Custom Base URL

If you need to point the SDK to a different URL (e.g. for testing), you can use WithBaseURL:

client, err := unsent.NewClient("un_xxx", unsent.WithBaseURL("https://api.example.com"))

Complete Example

Here's a complete example demonstrating multiple SDK features:

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "github.com/souravsspace/unsent-go/pkg/unsent"
)

func main() {
    // Initialize client
    client, err := unsent.NewClient("un_xxx")
    if err != nil {
        log.Fatal(err)
    }

    // Send an email with idempotency
    email, err := client.Emails.Send(
        unsent.SendEmailJSONBody{
            To:      toEmail("user@example.com"),
            From:    "hello@yourdomain.com",
            Subject: stringPtr("Welcome!"),
            Html:    stringPtr("<h1>Hello!</h1><p>Welcome to our service.</p>"),
        },
        unsent.WithIdempotencyKey("welcome-user-123"),
    )

    if err != nil {
        log.Printf("Error sending email: %v", err)
        return
    }

    fmt.Printf("โœ… Email sent! ID: %s\n", email.EmailID)

    // List recent emails
    emails, err := client.Emails.List(unsent.ListEmailsParams{
        Page:  stringPtr("1"),
        Limit: stringPtr("10"),
    })

    if err == nil {
        fmt.Printf("๐Ÿ“ง Found %d recent emails\n", len(*emails))
    }

    // Get analytics
    analytics, err := client.Analytics.Get()
    if err == nil {
        fmt.Printf("๐Ÿ“Š Sent: %d, Delivered: %d, Opened: %d\n",
            analytics.Sent, analytics.Delivered, analytics.Opened)
    }
}

// Helper functions
func stringPtr(s string) *string {
    return &s
}

func toEmail(email string) unsent.SendEmailJSONBody_To {
    var to unsent.SendEmailJSONBody_To
    json.Unmarshal([]byte(`"`+email+`"`), &to)
    return to
}

Learn More