eusend

package module
v0.11.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 13, 2026 License: MIT Imports: 12 Imported by: 0

README

eusend-go

Official Go SDK for the Eusend API — the EU-native transactional email platform.

Its shape mirrors resend-go, so migrating from Resend is largely a resendeusend rename.

  • NewClient + service methodsclient.Emails.Send(...), with WithContext variants.
  • Zero dependencies — standard library only.
  • Concurrency-safe — create one *Client and share it.
go get github.com/eusend-dev/eusend-go

Requires Go 1.21+.


Getting started

package main

import (
	"fmt"
	"log"

	eusend "github.com/eusend-dev/eusend-go"
)

func main() {
	client := eusend.NewClient("eu_live_...") // or NewClient("") to read EUSEND_API_KEY

	sent, err := client.Emails.Send(&eusend.SendEmailRequest{
		// From accepts a bare email or a display-name form: "Acme <you@yourdomain.com>"
		From:    "Acme <you@yourdomain.com>",
		To:      []string{"user@example.com"},
		Subject: "Hello",
		Html:    "<p>Hello world</p>",
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(sent.Id) // 9a8b7c6d-... (UUID)
}

Every method has a WithContext variant that takes a context.Context as its first argument (e.g. client.Emails.SendWithContext(ctx, params)). The context-free forms use context.Background().

Optional pointer fields (*bool, *string) have helpers: eusend.Bool(true), eusend.String("x").


Emails

Send

From and To are required; provide at least one of Html, Text, or TemplateId.

Field Type Notes
From string Verified domain; bare or display-name form.
To Cc Bcc ReplyTo []string Max 50 each.
Subject string
Html / Text string
TemplateId string Saved template.
Variables map[string]any Template substitutions (HTML-escaped).
Headers map[string]string No line breaks in names or values.
TrackOpens / TrackClicks *bool Nil uses your organization default (Settings → General → Email tracking); eusend.Bool(false) to disable.
Attachments []*Attachment Up to 20, 10 MB combined.
ScheduledAt string Future send, ≤ 30 days.
Attachments

Provide Content (raw bytes, base64-encoded on the wire) or Path (a public URL fetched at send time). Set ContentId for an inline <img src="cid:...">.

pdf, _ := os.ReadFile("invoice.pdf")

client.Emails.Send(&eusend.SendEmailRequest{
	From:    "you@yourdomain.com",
	To:      []string{"user@example.com"},
	Subject: "Your invoice",
	Html:    "<p>Attached.</p>",
	Attachments: []*eusend.Attachment{
		{Filename: "invoice.pdf", Content: pdf, ContentType: "application/pdf"},
	},
})
Idempotent sends
client.Emails.SendWithOptions(ctx, params, &eusend.SendEmailOptions{
	IdempotencyKey: "receipt-" + orderID,
})

Retrying with the same key never sends a duplicate and returns the original ID.

Scheduled sends

ScheduledAt accepts an ISO 8601 string or natural language ("in 1 hour", "tomorrow at 9am"), parsed server-side in UTC.

sent, _ := client.Emails.Send(&eusend.SendEmailRequest{
	From: "you@yourdomain.com", To: []string{"user@example.com"},
	Subject: "Reminder", Html: "<p>Soon.</p>",
	ScheduledAt: "in 1 hour",
})

client.Emails.Update(&eusend.UpdateEmailRequest{Id: sent.Id, ScheduledAt: "in 2 hours"})
client.Emails.Cancel(sent.Id)
Batch

Up to 100 emails in one request. Attachments and scheduling are stripped (not supported on the batch endpoint). Results map positionally to the input: queued items carry Id, rejected items carry Error and Code.

res, _ := client.Batch.Send([]*eusend.SendEmailRequest{
	{From: "you@yourdomain.com", To: []string{"alice@example.com"}, Subject: "Hi", Html: "<p>Hi</p>"},
	{From: "you@yourdomain.com", To: []string{"bob@example.com"},   Subject: "Hi", Html: "<p>Hi</p>"},
})
for _, r := range res.Data {
	if r.Id != "" {
		fmt.Println("queued", r.Id)
	} else {
		fmt.Printf("failed: %s (%s)\n", r.Error, r.Code)
	}
}
Retrieve & list
email, _ := client.Emails.Get("9a8b7c6d-...")
fmt.Println(email.Status, email.Events[0].Type)

page, _ := client.Emails.List(&eusend.ListEmailsOptions{Limit: 20, Status: "delivered"})
for _, e := range page.Data {
	fmt.Println(e.Id, e.Subject)
}
if page.NextCursor != "" {
	page, _ = client.Emails.List(&eusend.ListEmailsOptions{Cursor: page.NextCursor})
}

Statuses: queued scheduled sending sent delivered bounced complained suppressed failed.


Domains

created, _ := client.Domains.Create(&eusend.CreateDomainRequest{Name: "yourdomain.com"})
fmt.Println(created.Dkim.Name, created.Dkim.Value) // DNS records to add
fmt.Println(created.Spf, created.Dmarc)

client.Domains.Verify(created.Id) // after publishing the DNS records
client.Domains.List()
client.Domains.Get(created.Id)
client.Domains.Remove(created.Id)

API keys

key, _ := client.ApiKeys.Create(&eusend.CreateApiKeyRequest{Name: "Production"})
fmt.Println(key.Key) // eu_live_... — returned only once

client.ApiKeys.Create(&eusend.CreateApiKeyRequest{Name: "Sandbox", TestMode: true}) // eu_test_... key
client.ApiKeys.List()                                                               // prefixes only
client.ApiKeys.Remove(key.Id)

Emails sent with a test key are accepted and tracked but never delivered.

Permission defaults to PermissionFullAccess — every resource. PermissionSendingAccess limits the key to sending email (plus rescheduling and canceling a scheduled send); every other endpoint, including reading email logs, returns 403 FORBIDDEN. Such a key can also be pinned to one sending domain with DomainId, which is rejected on a full-access key.

client.ApiKeys.Create(&eusend.CreateApiKeyRequest{
	Name:       "Billing service",
	Permission: eusend.PermissionSendingAccess,
	DomainId:   domainId, // omit for any verified domain
})

Deleting a domain revokes every key restricted to it.


Audiences & contacts

Contact operations are grouped under Audiences (they live under a specific audience).

audience, _ := client.Audiences.Create(&eusend.CreateAudienceRequest{Name: "Newsletter"})

client.Audiences.CreateContact(audience.Id, &eusend.CreateContactRequest{
	Email: "user@example.com", FirstName: "Jane",
	Properties: map[string]string{"plan": "pro"}, // becomes {{plan}} in a broadcast
})

// Bulk upsert (up to 1,000)
client.Audiences.BatchCreateContacts(audience.Id, []*eusend.CreateContactRequest{
	{Email: "alice@example.com", FirstName: "Alice"},
	{Email: "bob@example.com", FirstName: "Bob"},
})

page, _ := client.Audiences.ListContacts(audience.Id, &eusend.ListContactsOptions{
	Subscribed: eusend.Bool(true), Search: "gmail.com",
})
contact := page.Data[0]

client.Audiences.UpdateContact(audience.Id, contact.Id, &eusend.UpdateContactRequest{
	Unsubscribed: eusend.Bool(true),
})
client.Audiences.GetContact(audience.Id, contact.Id)
client.Audiences.RemoveContact(audience.Id, contact.Id)

client.Audiences.List()
client.Audiences.Remove(audience.Id)

Suppressions

Addresses the account will not send to. Hard bounces and spam complaints are added automatically; these methods cover the ones you manage yourself. A send to a suppressed address is skipped and recorded with status suppressed; if every recipient is suppressed the send fails with ALL_SUPPRESSED.

Test-mode keys can read the list but not modify it.

// Everything suppressed for a hard bounce, or at one domain
page, _ := client.Suppressions.List(&eusend.ListSuppressionsOptions{
	Reason: eusend.SuppressionReasonBounce, Limit: 50,
})
client.Suppressions.List(&eusend.ListSuppressionsOptions{Email: "@acme.com"})

// Suppress an address. Already suppressed? The existing entry comes back unchanged —
// a manual add never rewrites a real bounce or complaint.
client.Suppressions.Create(&eusend.CreateSuppressionRequest{Email: "opted-out@example.com"})

// Import up to 1,000 at a time — do this before your first send when migrating, so
// addresses that already bounced elsewhere don't get a fresh attempt from a new IP.
res, _ := client.Suppressions.Import([]*eusend.SuppressionImportItem{
	{Email: "one@example.com"},
	{Email: "two@example.com", Reason: eusend.SuppressionReasonComplaint},
})
fmt.Println(res.Count, res.AlreadySuppressed, res.Duplicates)

// Un-suppress by entry id or by address
client.Suppressions.Remove("invalid@example.com")

// The whole list as CSV
csv, _ := client.Suppressions.Export()

_ = page

Templates

{{variable}} placeholders are substituted at send time; values are HTML-escaped.

tpl, _ := client.Templates.Create(&eusend.CreateTemplateRequest{
	Name:    "Welcome email",
	Subject: "Welcome, {{name}}!",
	Html:    "<h1>Hi {{name}}</h1><p>Welcome to {{product}}.</p>",
})

client.Emails.Send(&eusend.SendEmailRequest{
	From: "you@yourdomain.com", To: []string{"user@example.com"},
	TemplateId: tpl.Id,
	Variables:  map[string]any{"name": "Jane", "product": "Acme"},
})

client.Templates.List()
client.Templates.Get(tpl.Id)
client.Templates.Update(tpl.Id, &eusend.UpdateTemplateRequest{Subject: eusend.String("New subject")})
client.Templates.Remove(tpl.Id)

Webhooks

hook, _ := client.Webhooks.Create(&eusend.CreateWebhookRequest{
	Url:    "https://yourapp.com/webhooks/eusend",
	Events: []string{"email.delivered", "email.bounced", "email.complained"}, // or []string{"*"}
})
fmt.Println(hook.Secret) // signing secret — returned only once

client.Webhooks.List()
client.Webhooks.Get(hook.Id) // includes recent deliveries
client.Webhooks.Update(hook.Id, &eusend.UpdateWebhookRequest{Events: []string{"email.bounced"}})
client.Webhooks.Remove(hook.Id)

Events: email.sent email.delivered email.bounced email.complained email.opened email.clicked. The endpoint must be a public http(s) URL returning 2xx directly (redirects count as failures).

Verifying signatures

Every delivery is signed with HMAC-SHA256 over {webhook-id}.{webhook-timestamp}.{body}:

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
)

func verify(r *http.Request, body []byte, secret string) bool {
	signed := r.Header.Get("webhook-id") + "." + r.Header.Get("webhook-timestamp") + "." + string(body)
	mac := hmac.New(sha256.New, []byte(secret))
	mac.Write([]byte(signed))
	expected := "v1," + base64.StdEncoding.EncodeToString(mac.Sum(nil))
	return hmac.Equal([]byte(r.Header.Get("webhook-signature")), []byte(expected))
}

Broadcasts

Send one email to every contact in an audience. {{first_name}}, {{last_name}}, {{full_name}}, and {{email}} are available per recipient, and RFC 8058 one-click unsubscribe headers are added automatically.

bc, _ := client.Broadcasts.Create(&eusend.CreateBroadcastRequest{
	Name:       "May newsletter",
	AudienceId: audience.Id,
	From:       "Sivert <hello@yourdomain.com>",
	Subject:    "May update",
	Html:       "<p>Hi {{first_name}}, your monthly update is here...</p>",
})

client.Broadcasts.Send(bc.Id, nil)                                          // send now
client.Broadcasts.Send(bc.Id, &eusend.SendBroadcastRequest{ScheduledAt: "2026-06-01T09:00:00Z"}) // or schedule
client.Broadcasts.Cancel(bc.Id)

client.Broadcasts.List()
client.Broadcasts.Get(bc.Id) // includes delivery stats
client.Broadcasts.Update(bc.Id, &eusend.UpdateBroadcastRequest{Subject: eusend.String("Updated")})
client.Broadcasts.Remove(bc.Id)

Calling Send on a paused broadcast resumes it from where it stopped.


Error handling

Every method returns (result, error). Any non-2xx response, and any network failure, is an *eusend.Error:

sent, err := client.Emails.Send(params)
if err != nil {
	var apiErr *eusend.Error
	if errors.As(err, &apiErr) {
		fmt.Println(apiErr.Code)       // "MONTHLY_LIMIT_EXCEEDED"
		fmt.Println(apiErr.Message)    // "Monthly send limit exceeded"
		fmt.Println(apiErr.StatusCode) // 429 (0 for a network failure)

		if apiErr.Code == eusend.CodeMonthlyLimitExceeded {
			// back off and retry later
		}
	}
	return
}

On a 429, apiErr.RetryAfter, RateLimitReset, and RateLimitRemaining are populated from the response headers.

Code constant Wire value Status
CodeUnauthorized UNAUTHORIZED 401
CodeForbidden FORBIDDEN 403
CodeNotFound NOT_FOUND 404
CodeValidationError VALIDATION_ERROR 400
CodeBadRequest BAD_REQUEST 400
CodeConflict CONFLICT 409
CodeRateLimited RATE_LIMITED 429
CodeMonthlyLimitExceeded MONTHLY_LIMIT_EXCEEDED 429
CodeDailyLimitExceeded DAILY_LIMIT_EXCEEDED 429
CodePlanLimitExceeded PLAN_LIMIT_EXCEEDED 403
CodeDomainNotVerified DOMAIN_NOT_VERIFIED 403
CodeSendingSuspended SENDING_SUSPENDED 403
CodeListSendHeld LIST_SEND_HELD 403
CodeBroadcastHeld BROADCAST_HELD 403
CodeAllSuppressed ALL_SUPPRESSED 422
CodeServicePaused SERVICE_PAUSED 503
CodeInternalError INTERNAL_ERROR 500
CodeApplicationError application_error — (network failure)

Configuration

// Custom http.Client (timeouts, proxies, ...):
client := eusend.NewCustomClient(&http.Client{Timeout: 60 * time.Second}, "eu_live_...")

// Override the base URL (e.g. for testing) after construction:
client.BaseURL, _ = url.Parse("https://api.eusend.dev/")

Documentation

Overview

Package eusend is the official Go SDK for the Eusend API — the EU-native transactional email platform. Its shape mirrors github.com/resend/resend-go, so migrating from Resend is largely a `resend` → `eusend` rename.

client := eusend.NewClient("eu_live_...")
sent, err := client.Emails.Send(&eusend.SendEmailRequest{
    From:    "Acme <you@yourdomain.com>",
    To:      []string{"user@example.com"},
    Subject: "Hello",
    Html:    "<p>Hello world</p>",
})

Index

Constants

View Source
const (
	PermissionFullAccess    = "full_access"
	PermissionSendingAccess = "sending_access"
)

What a key may reach: PermissionFullAccess for every resource, PermissionSendingAccess for sending email only.

View Source
const (
	CodeUnauthorized                 = "UNAUTHORIZED"
	CodeForbidden                    = "FORBIDDEN"
	CodeNotFound                     = "NOT_FOUND"
	CodeValidationError              = "VALIDATION_ERROR"
	CodeBadRequest                   = "BAD_REQUEST"
	CodePayloadTooLarge              = "PAYLOAD_TOO_LARGE"
	CodeConflict                     = "CONFLICT"
	CodeRateLimited                  = "RATE_LIMITED"
	CodeMonthlyLimitExceeded         = "MONTHLY_LIMIT_EXCEEDED"
	CodeDailyLimitExceeded           = "DAILY_LIMIT_EXCEEDED"
	CodePlanLimitExceeded            = "PLAN_LIMIT_EXCEEDED"
	CodeDomainNotVerified            = "DOMAIN_NOT_VERIFIED"
	CodeSendingSuspended             = "SENDING_SUSPENDED"
	CodeAccountRestricted            = "ACCOUNT_RESTRICTED"
	CodeSenderNotPermitted           = "SENDER_NOT_PERMITTED"
	CodeListSendHeld                 = "LIST_SEND_HELD"
	CodeBroadcastHeld                = "BROADCAST_HELD"
	CodeAllSuppressed                = "ALL_SUPPRESSED"
	CodeRecipientDomainUndeliverable = "RECIPIENT_DOMAIN_UNDELIVERABLE"
	CodeAttachmentStorageErr         = "ATTACHMENT_STORAGE_ERROR"
	CodeServicePaused                = "SERVICE_PAUSED"
	CodeInternalError                = "INTERNAL_ERROR"
	CodeApplicationError             = "application_error"
)

Error codes returned by the API. CodeApplicationError is SDK-only and signals that the request never reached the server (network failure, DNS, timeout). CodeRecipientDomainUndeliverable means a recipient's domain publishes no mail exchanger, so the message could never be delivered — usually a typo.

View Source
const (
	SuppressionReasonBounce    = "bounce"
	SuppressionReasonComplaint = "complaint"
	SuppressionReasonManual    = "manual"
)

Suppression reasons. Bounce and complaint are written automatically by the platform; manual is what you add yourself.

Variables

View Source
var (
	ErrFailedToCreateRequest = errors.New("[ERROR]: Failed to create request")
)

Sentinel errors returned when a request object cannot be constructed.

Functions

func Bool

func Bool(v bool) *bool

Bool returns a pointer to v — a convenience for optional *bool fields.

func String

func String(v string) *string

String returns a pointer to v — a convenience for optional *string fields.

Types

type ApiKey

type ApiKey struct {
	Id         string `json:"id"`
	Name       string `json:"name"`
	Prefix     string `json:"prefix"`
	TestMode   bool   `json:"test_mode"`
	Permission string `json:"permission"`
	DomainId   string `json:"domain_id"`
	DomainName string `json:"domain_name"`
	CreatedAt  string `json:"created_at"`
	LastUsedAt string `json:"last_used_at"`
}

ApiKey is a row from ApiKeys.List. The full key is never returned after creation.

type ApiKeysSvc

type ApiKeysSvc interface {
	Create(params *CreateApiKeyRequest) (*CreateApiKeyResponse, error)
	CreateWithContext(ctx context.Context, params *CreateApiKeyRequest) (*CreateApiKeyResponse, error)
	List() ([]ApiKey, error)
	ListWithContext(ctx context.Context) ([]ApiKey, error)
	Remove(apiKeyId string) (*GenericResponse, error)
	RemoveWithContext(ctx context.Context, apiKeyId string) (*GenericResponse, error)
}

ApiKeysSvc is the /api-keys API.

type ApiKeysSvcImpl

type ApiKeysSvcImpl struct {
	// contains filtered or unexported fields
}

func (*ApiKeysSvcImpl) Create

func (*ApiKeysSvcImpl) CreateWithContext

func (s *ApiKeysSvcImpl) CreateWithContext(ctx context.Context, params *CreateApiKeyRequest) (*CreateApiKeyResponse, error)

func (*ApiKeysSvcImpl) List

func (s *ApiKeysSvcImpl) List() ([]ApiKey, error)

func (*ApiKeysSvcImpl) ListWithContext

func (s *ApiKeysSvcImpl) ListWithContext(ctx context.Context) ([]ApiKey, error)

func (*ApiKeysSvcImpl) Remove

func (s *ApiKeysSvcImpl) Remove(apiKeyId string) (*GenericResponse, error)

func (*ApiKeysSvcImpl) RemoveWithContext

func (s *ApiKeysSvcImpl) RemoveWithContext(ctx context.Context, apiKeyId string) (*GenericResponse, error)

type Attachment

type Attachment struct {
	Content     []byte `json:"content,omitempty"`
	Filename    string `json:"filename,omitempty"`
	Path        string `json:"path,omitempty"`
	ContentType string `json:"content_type,omitempty"`
	ContentId   string `json:"content_id,omitempty"`
}

Attachment is a file attached to an email. Provide either Content (raw bytes, base64-encoded on the wire) or Path (a public URL fetched at send time). Set ContentId for an inline attachment referenced from HTML with <img src="cid:...">.

type Audience

type Audience struct {
	Id             string `json:"id"`
	Name           string `json:"name"`
	OrganizationId string `json:"organizationId"`
	CreatedAt      string `json:"createdAt"`
	UpdatedAt      string `json:"updatedAt"`
}

Audience is returned by Audiences.Create.

type AudienceListItem

type AudienceListItem struct {
	Id           string `json:"id"`
	Name         string `json:"name"`
	CreatedAt    string `json:"createdAt"`
	ContactCount int    `json:"contactCount"`
}

AudienceListItem is a row from Audiences.List.

type AudiencesSvc

type AudiencesSvc interface {
	Create(params *CreateAudienceRequest) (*Audience, error)
	CreateWithContext(ctx context.Context, params *CreateAudienceRequest) (*Audience, error)
	List() ([]AudienceListItem, error)
	ListWithContext(ctx context.Context) ([]AudienceListItem, error)
	Remove(audienceId string) (*GenericResponse, error)
	RemoveWithContext(ctx context.Context, audienceId string) (*GenericResponse, error)

	CreateContact(audienceId string, params *CreateContactRequest) (*Contact, error)
	CreateContactWithContext(ctx context.Context, audienceId string, params *CreateContactRequest) (*Contact, error)
	BatchCreateContacts(audienceId string, contacts []*CreateContactRequest) (*BatchCreateContactsResponse, error)
	BatchDeleteContacts(audienceId string, contactIds []string) (*BatchDeleteContactsResponse, error)
	BatchDeleteContactsWithContext(ctx context.Context, audienceId string, contactIds []string) (*BatchDeleteContactsResponse, error)
	BatchCreateContactsWithContext(ctx context.Context, audienceId string, contacts []*CreateContactRequest) (*BatchCreateContactsResponse, error)
	ListContacts(audienceId string, options *ListContactsOptions) (*ListContactsResponse, error)
	ListContactsWithContext(ctx context.Context, audienceId string, options *ListContactsOptions) (*ListContactsResponse, error)
	GetContact(audienceId, contactId string) (*Contact, error)
	GetContactWithContext(ctx context.Context, audienceId, contactId string) (*Contact, error)
	UpdateContact(audienceId, contactId string, params *UpdateContactRequest) (*Contact, error)
	UpdateContactWithContext(ctx context.Context, audienceId, contactId string, params *UpdateContactRequest) (*Contact, error)
	RemoveContact(audienceId, contactId string) (*GenericResponse, error)
	RemoveContactWithContext(ctx context.Context, audienceId, contactId string) (*GenericResponse, error)
}

AudiencesSvc is the /audiences API, including nested contact operations.

type AudiencesSvcImpl

type AudiencesSvcImpl struct {
	// contains filtered or unexported fields
}

func (*AudiencesSvcImpl) BatchCreateContacts

func (s *AudiencesSvcImpl) BatchCreateContacts(audienceId string, contacts []*CreateContactRequest) (*BatchCreateContactsResponse, error)

func (*AudiencesSvcImpl) BatchCreateContactsWithContext

func (s *AudiencesSvcImpl) BatchCreateContactsWithContext(ctx context.Context, audienceId string, contacts []*CreateContactRequest) (*BatchCreateContactsResponse, error)

func (*AudiencesSvcImpl) BatchDeleteContacts added in v0.10.0

func (s *AudiencesSvcImpl) BatchDeleteContacts(audienceId string, contactIds []string) (*BatchDeleteContactsResponse, error)

BatchDeleteContacts removes up to 1,000 contacts from an audience by id.

This is not an unsubscribe: it removes them from the audience without adding them to the suppression list. Use UpdateContact with Unsubscribed to stop mailing somebody while keeping the record.

func (*AudiencesSvcImpl) BatchDeleteContactsWithContext added in v0.10.0

func (s *AudiencesSvcImpl) BatchDeleteContactsWithContext(ctx context.Context, audienceId string, contactIds []string) (*BatchDeleteContactsResponse, error)

func (*AudiencesSvcImpl) Create

func (s *AudiencesSvcImpl) Create(params *CreateAudienceRequest) (*Audience, error)

func (*AudiencesSvcImpl) CreateContact

func (s *AudiencesSvcImpl) CreateContact(audienceId string, params *CreateContactRequest) (*Contact, error)

func (*AudiencesSvcImpl) CreateContactWithContext

func (s *AudiencesSvcImpl) CreateContactWithContext(ctx context.Context, audienceId string, params *CreateContactRequest) (*Contact, error)

func (*AudiencesSvcImpl) CreateWithContext

func (s *AudiencesSvcImpl) CreateWithContext(ctx context.Context, params *CreateAudienceRequest) (*Audience, error)

func (*AudiencesSvcImpl) GetContact

func (s *AudiencesSvcImpl) GetContact(audienceId, contactId string) (*Contact, error)

func (*AudiencesSvcImpl) GetContactWithContext

func (s *AudiencesSvcImpl) GetContactWithContext(ctx context.Context, audienceId, contactId string) (*Contact, error)

func (*AudiencesSvcImpl) List

func (s *AudiencesSvcImpl) List() ([]AudienceListItem, error)

func (*AudiencesSvcImpl) ListContacts

func (s *AudiencesSvcImpl) ListContacts(audienceId string, options *ListContactsOptions) (*ListContactsResponse, error)

func (*AudiencesSvcImpl) ListContactsWithContext

func (s *AudiencesSvcImpl) ListContactsWithContext(ctx context.Context, audienceId string, options *ListContactsOptions) (*ListContactsResponse, error)

func (*AudiencesSvcImpl) ListWithContext

func (s *AudiencesSvcImpl) ListWithContext(ctx context.Context) ([]AudienceListItem, error)

func (*AudiencesSvcImpl) Remove

func (s *AudiencesSvcImpl) Remove(audienceId string) (*GenericResponse, error)

func (*AudiencesSvcImpl) RemoveContact

func (s *AudiencesSvcImpl) RemoveContact(audienceId, contactId string) (*GenericResponse, error)

func (*AudiencesSvcImpl) RemoveContactWithContext

func (s *AudiencesSvcImpl) RemoveContactWithContext(ctx context.Context, audienceId, contactId string) (*GenericResponse, error)

func (*AudiencesSvcImpl) RemoveWithContext

func (s *AudiencesSvcImpl) RemoveWithContext(ctx context.Context, audienceId string) (*GenericResponse, error)

func (*AudiencesSvcImpl) UpdateContact

func (s *AudiencesSvcImpl) UpdateContact(audienceId, contactId string, params *UpdateContactRequest) (*Contact, error)

func (*AudiencesSvcImpl) UpdateContactWithContext

func (s *AudiencesSvcImpl) UpdateContactWithContext(ctx context.Context, audienceId, contactId string, params *UpdateContactRequest) (*Contact, error)

type BatchCreateContactsResponse

type BatchCreateContactsResponse struct {
	Count int `json:"count"`
	// Duplicates is how many repeated addresses were collapsed to reach Count, which
	// is what explains a count lower than the number of rows sent.
	Duplicates int `json:"duplicates"`
}

BatchCreateContactsResponse reports how many contacts were written.

type BatchDeleteContactsResponse added in v0.10.0

type BatchDeleteContactsResponse struct {
	Deleted int `json:"deleted"`
}

BatchDeleteContactsResponse reports how many contacts were removed. Deleted may be lower than the number of ids sent -- an id may already be gone, or may belong to a different audience -- so a retry after a dropped response settles at 0.

type BatchEmailResponse

type BatchEmailResponse struct {
	Data []BatchItemResult `json:"data"`
}

BatchEmailResponse is the response from Batch.Send.

type BatchItemResult

type BatchItemResult struct {
	Id    string `json:"id,omitempty"`
	Error string `json:"error,omitempty"`
	Code  string `json:"code,omitempty"`
}

BatchItemResult is one outcome of a batch send, positionally mapped to the input slice: result[i] describes params[i]. Queued items carry Id; rejected items carry Error and Code. Branch on `if result.Id != ""`.

type BatchSvc

type BatchSvc interface {
	Send(params []*SendEmailRequest) (*BatchEmailResponse, error)
	SendWithContext(ctx context.Context, params []*SendEmailRequest) (*BatchEmailResponse, error)
}

BatchSvc is the /emails/batch API.

type BatchSvcImpl

type BatchSvcImpl struct {
	// contains filtered or unexported fields
}

func (*BatchSvcImpl) Send

func (s *BatchSvcImpl) Send(params []*SendEmailRequest) (*BatchEmailResponse, error)

Send sends up to 100 emails in a single request. Attachments and scheduling are not supported on the batch endpoint and are stripped from each item — send those individually via Emails.Send. A failed item never fails the whole batch; branch on the presence of Id per returned result.

func (*BatchSvcImpl) SendWithContext

func (s *BatchSvcImpl) SendWithContext(ctx context.Context, params []*SendEmailRequest) (*BatchEmailResponse, error)

type Broadcast

type Broadcast struct {
	Id                string            `json:"id"`
	OrganizationId    string            `json:"organizationId"`
	Name              string            `json:"name"`
	Status            string            `json:"status"`
	AudienceId        *string           `json:"audienceId"`
	FromAddress       string            `json:"fromAddress"`
	ReplyTo           *string           `json:"replyTo"`
	Subject           string            `json:"subject"`
	Html              *string           `json:"html"`
	ReactSource       *string           `json:"reactSource"`
	EditorJson        map[string]any    `json:"editorJson"`
	TemplateId        *string           `json:"templateId"`
	TemplateVariables map[string]string `json:"templateVariables"`
	HeldReason        *string           `json:"heldReason"`
	ScheduledAt       *string           `json:"scheduledAt"`
	TrackOpens        bool              `json:"trackOpens"`
	TrackClicks       bool              `json:"trackClicks"`
	RecipientCount    int               `json:"recipientCount"`
	SentCount         int               `json:"sentCount"`
	StartedAt         *string           `json:"startedAt"`
	CompletedAt       *string           `json:"completedAt"`
	// Stats is populated only by Broadcasts.Get.
	Stats     map[string]int `json:"stats"`
	CreatedAt string         `json:"createdAt"`
	UpdatedAt string         `json:"updatedAt"`
}

Broadcast is returned by Broadcasts.Create/Update/Cancel and (with stats) Get.

type BroadcastListItem

type BroadcastListItem struct {
	Id             string  `json:"id"`
	Name           string  `json:"name"`
	Status         string  `json:"status"`
	AudienceId     *string `json:"audienceId"`
	FromAddress    string  `json:"fromAddress"`
	Subject        string  `json:"subject"`
	RecipientCount int     `json:"recipientCount"`
	SentCount      int     `json:"sentCount"`
	ScheduledAt    *string `json:"scheduledAt"`
	StartedAt      *string `json:"startedAt"`
	CompletedAt    *string `json:"completedAt"`
	CreatedAt      string  `json:"createdAt"`
	AudienceName   *string `json:"audienceName"`
}

BroadcastListItem is a row from Broadcasts.List.

type BroadcastsSvc

type BroadcastsSvc interface {
	Create(params *CreateBroadcastRequest) (*Broadcast, error)
	CreateWithContext(ctx context.Context, params *CreateBroadcastRequest) (*Broadcast, error)
	List() ([]BroadcastListItem, error)
	ListWithContext(ctx context.Context) ([]BroadcastListItem, error)
	Get(broadcastId string) (*Broadcast, error)
	GetWithContext(ctx context.Context, broadcastId string) (*Broadcast, error)
	Update(broadcastId string, params *UpdateBroadcastRequest) (*Broadcast, error)
	UpdateWithContext(ctx context.Context, broadcastId string, params *UpdateBroadcastRequest) (*Broadcast, error)
	Send(broadcastId string, params *SendBroadcastRequest) (*SendBroadcastResponse, error)
	SendWithContext(ctx context.Context, broadcastId string, params *SendBroadcastRequest) (*SendBroadcastResponse, error)
	Test(broadcastId string, params *TestBroadcastRequest) (*TestBroadcastResponse, error)
	TestWithContext(ctx context.Context, broadcastId string, params *TestBroadcastRequest) (*TestBroadcastResponse, error)
	Cancel(broadcastId string) (*Broadcast, error)
	CancelWithContext(ctx context.Context, broadcastId string) (*Broadcast, error)
	Remove(broadcastId string) (*GenericResponse, error)
	RemoveWithContext(ctx context.Context, broadcastId string) (*GenericResponse, error)
}

BroadcastsSvc is the /broadcasts API.

type BroadcastsSvcImpl

type BroadcastsSvcImpl struct {
	// contains filtered or unexported fields
}

func (*BroadcastsSvcImpl) Cancel

func (s *BroadcastsSvcImpl) Cancel(broadcastId string) (*Broadcast, error)

func (*BroadcastsSvcImpl) CancelWithContext

func (s *BroadcastsSvcImpl) CancelWithContext(ctx context.Context, broadcastId string) (*Broadcast, error)

func (*BroadcastsSvcImpl) Create

func (*BroadcastsSvcImpl) CreateWithContext

func (s *BroadcastsSvcImpl) CreateWithContext(ctx context.Context, params *CreateBroadcastRequest) (*Broadcast, error)

func (*BroadcastsSvcImpl) Get

func (s *BroadcastsSvcImpl) Get(broadcastId string) (*Broadcast, error)

func (*BroadcastsSvcImpl) GetWithContext

func (s *BroadcastsSvcImpl) GetWithContext(ctx context.Context, broadcastId string) (*Broadcast, error)

func (*BroadcastsSvcImpl) List

func (s *BroadcastsSvcImpl) List() ([]BroadcastListItem, error)

func (*BroadcastsSvcImpl) ListWithContext

func (s *BroadcastsSvcImpl) ListWithContext(ctx context.Context) ([]BroadcastListItem, error)

func (*BroadcastsSvcImpl) Remove

func (s *BroadcastsSvcImpl) Remove(broadcastId string) (*GenericResponse, error)

func (*BroadcastsSvcImpl) RemoveWithContext

func (s *BroadcastsSvcImpl) RemoveWithContext(ctx context.Context, broadcastId string) (*GenericResponse, error)

func (*BroadcastsSvcImpl) Send

func (s *BroadcastsSvcImpl) Send(broadcastId string, params *SendBroadcastRequest) (*SendBroadcastResponse, error)

Send sends a broadcast immediately, or schedules it when params.ScheduledAt is set. Calling Send on a paused broadcast resumes it from where it stopped.

func (*BroadcastsSvcImpl) SendWithContext

func (s *BroadcastsSvcImpl) SendWithContext(ctx context.Context, broadcastId string, params *SendBroadcastRequest) (*SendBroadcastResponse, error)

func (*BroadcastsSvcImpl) Test added in v0.8.0

func (s *BroadcastsSvcImpl) Test(broadcastId string, params *TestBroadcastRequest) (*TestBroadcastResponse, error)

Test sends a copy of the broadcast to your own verified addresses — the real message through the real sending path, so it shows what a recipient will see.

Unlike Send it works on every plan including Free. It costs daily and monthly quota like any other send, and does not move the broadcast's status: the campaign stays a draft no matter how many tests you send.

Requires a LIVE api key. "Test" here means a dress rehearsal, not a sandbox — an eu_test_ key is refused because the mail really is delivered.

func (*BroadcastsSvcImpl) TestWithContext added in v0.8.0

func (s *BroadcastsSvcImpl) TestWithContext(ctx context.Context, broadcastId string, params *TestBroadcastRequest) (*TestBroadcastResponse, error)

func (*BroadcastsSvcImpl) Update

func (s *BroadcastsSvcImpl) Update(broadcastId string, params *UpdateBroadcastRequest) (*Broadcast, error)

func (*BroadcastsSvcImpl) UpdateWithContext

func (s *BroadcastsSvcImpl) UpdateWithContext(ctx context.Context, broadcastId string, params *UpdateBroadcastRequest) (*Broadcast, error)

type CancelScheduledEmailResponse

type CancelScheduledEmailResponse struct {
	Id     string `json:"id"`
	Status string `json:"status"`
}

CancelScheduledEmailResponse is the response from Emails.Cancel.

type Client

type Client struct {
	ApiKey    string
	BaseURL   *url.URL
	UserAgent string

	Emails       EmailsSvc
	Batch        BatchSvc
	ApiKeys      ApiKeysSvc
	Domains      DomainsSvc
	Audiences    AudiencesSvc
	Templates    TemplatesSvc
	Webhooks     WebhooksSvc
	Broadcasts   BroadcastsSvc
	Suppressions SuppressionsSvc
	// contains filtered or unexported fields
}

Client is the entry point to the Eusend API. Create one with NewClient and share it across goroutines — it is safe for concurrent use.

func NewClient

func NewClient(apiKey string) *Client

NewClient creates a Client with the given API key. If apiKey is empty, the EUSEND_API_KEY environment variable is used.

func NewCustomClient

func NewCustomClient(httpClient *http.Client, apiKey string) *Client

NewCustomClient creates a Client with a custom *http.Client (for custom timeouts, proxies, etc.).

func (*Client) NewRequest

func (c *Client) NewRequest(ctx context.Context, method, path string, params any) (*http.Request, error)

NewRequest builds an *http.Request against the API, JSON-encoding params when non-nil.

func (*Client) NewRequestWithOptions

func (c *Client) NewRequestWithOptions(ctx context.Context, method, path string, params any, options Options) (*http.Request, error)

NewRequestWithOptions is NewRequest plus per-call options such as an idempotency key.

func (*Client) Perform

func (c *Client) Perform(req *http.Request, ret any) (*http.Response, error)

Perform sends req and decodes a 2xx JSON body into ret (which may be nil). Non-2xx responses are converted to *Error via handleError.

func (*Client) PerformRaw added in v0.3.0

func (c *Client) PerformRaw(req *http.Request) ([]byte, error)

PerformRaw sends req and returns the raw 2xx body. Used for the endpoints that answer with something other than JSON (the suppression list export is CSV), where Perform's decode step would reject a perfectly good response.

type Contact

type Contact struct {
	Id         string `json:"id"`
	AudienceId string `json:"audienceId"`
	Email      string `json:"email"`
	FirstName  string `json:"firstName"`
	LastName   string `json:"lastName"`
	// Properties are the contact's custom properties, available as {{key}} in a
	// broadcast body.
	Properties     map[string]string `json:"properties"`
	Status         string            `json:"status"`
	UnsubscribedAt string            `json:"unsubscribedAt"`
	CreatedAt      string            `json:"createdAt"`
	UpdatedAt      string            `json:"updatedAt"`
}

Contact is a member of an audience.

type CreateApiKeyRequest

type CreateApiKeyRequest struct {
	Name string `json:"name"`
	// TestMode issues a sandbox key. Emails sent with a test key are accepted
	// and tracked but never delivered.
	TestMode bool `json:"test_mode"`
	// Permission defaults to PermissionFullAccess when empty.
	Permission string `json:"permission,omitempty"`
	// DomainId restricts the key to sending from a single domain. Only valid
	// alongside PermissionSendingAccess; leave empty for any verified domain.
	DomainId string `json:"domain_id,omitempty"`
}

CreateApiKeyRequest is the request object for ApiKeys.Create.

type CreateApiKeyResponse

type CreateApiKeyResponse struct {
	Id         string `json:"id"`
	Name       string `json:"name"`
	Key        string `json:"key"`
	Prefix     string `json:"prefix"`
	TestMode   bool   `json:"test_mode"`
	Permission string `json:"permission"`
	DomainId   string `json:"domain_id"`
	DomainName string `json:"domain_name"`
	CreatedAt  string `json:"created_at"`
}

CreateApiKeyResponse is returned by ApiKeys.Create. Key holds the full secret and is returned only once — store it securely.

type CreateAudienceRequest

type CreateAudienceRequest struct {
	Name string `json:"name"`
}

CreateAudienceRequest is the request object for Audiences.Create.

type CreateBroadcastRequest

type CreateBroadcastRequest struct {
	Name              string            `json:"name"`
	AudienceId        string            `json:"audience_id"`
	From              string            `json:"from"`
	Subject           string            `json:"subject"`
	Html              string            `json:"html,omitempty"`
	TemplateId        string            `json:"template_id,omitempty"`
	TemplateVariables map[string]string `json:"template_variables,omitempty"`
	// TrackOpens/TrackClicks are pointers so that an explicit false is sent rather than
	// dropped by omitempty. Nil means "use the organization default".
	TrackOpens  *bool `json:"track_opens,omitempty"`
	TrackClicks *bool `json:"track_clicks,omitempty"`
}

CreateBroadcastRequest is the request object for Broadcasts.Create. Provide either Html or TemplateId.

type CreateContactRequest

type CreateContactRequest struct {
	Email     string `json:"email"`
	FirstName string `json:"first_name,omitempty"`
	LastName  string `json:"last_name,omitempty"`

	// Unsubscribed and CreatedAt are accepted by BatchCreateContacts only — they exist
	// for migrating a list in from another provider. CreateContact rejects them.
	//
	// Unsubscribed marks the contact as opted out. An import can only ever ADD an
	// opt-out: false will not re-subscribe someone who has already unsubscribed, which
	// is a consent decision and stays on UpdateContact.
	Unsubscribed *bool `json:"unsubscribed,omitempty"`
	// CreatedAt is the original signup time (ISO 8601). Applied on insert only; an
	// existing contact keeps the date it already has.
	CreatedAt string `json:"created_at,omitempty"`

	// Properties are custom properties merged into the {{variable}} map when a
	// broadcast renders, so {"plan": "pro"} makes {{plan}} resolve to pro.
	//
	// Keys are lowercase letters, digits and underscores, starting with a letter (at
	// most 40 characters, 20 properties per contact). "email", "name", "first_name",
	// "last_name" and "full_name" are built in and cannot be used.
	//
	// CreateContact REPLACES the contact's properties with these; BatchCreateContacts
	// MERGES them, so an import carrying only "plan" will not drop a "company" an
	// earlier import set.
	Properties map[string]string `json:"properties,omitempty"`
}

CreateContactRequest is the request object for Audiences.CreateContact and the item shape for BatchCreateContacts.

type CreateDomainRequest

type CreateDomainRequest struct {
	Name string `json:"name"`
}

CreateDomainRequest is the request object for Domains.Create.

type CreateDomainResponse

type CreateDomainResponse struct {
	Id   string `json:"id"`
	Name string `json:"name"`
	// Records is every record to publish, in presentation order. Prefer it over the
	// individual fields below — it is the only place the optional Return-Path
	// alignment records appear.
	Records []DnsRecord `json:"records"`
	Dkim    DnsRecord   `json:"dkim"`
	Dmarc   DnsRecord   `json:"dmarc"`
}

CreateDomainResponse is returned by Domains.Create and carries the DNS records to add.

type CreateSuppressionRequest added in v0.3.0

type CreateSuppressionRequest struct {
	Email string `json:"email"`
	// Reason defaults to "manual" when empty. An add never overwrites the reason an
	// address is already suppressed for.
	Reason string `json:"reason,omitempty"`
}

CreateSuppressionRequest is the request object for Suppressions.Create.

type CreateTemplateRequest

type CreateTemplateRequest struct {
	Name    string `json:"name"`
	Subject string `json:"subject"`
	Html    string `json:"html"`
}

CreateTemplateRequest is the request object for Templates.Create. Use {{variable}} placeholders in Subject/Html; values are HTML-escaped at send time.

type CreateWebhookRequest

type CreateWebhookRequest struct {
	Url    string   `json:"url"`
	Events []string `json:"events"`
}

CreateWebhookRequest is the request object for Webhooks.Create. Pass Events []string{"*"} to subscribe to every event.

type DnsRecord

type DnsRecord struct {
	Type        string `json:"type"`
	Name        string `json:"name"`
	Value       string `json:"value"`
	Priority    int    `json:"priority,omitempty"` // MX records only
	Purpose     string `json:"purpose,omitempty"`  // authentication | policy | alignment
	Description string `json:"description,omitempty"`
}

DnsRecord is a DNS entry to publish for a domain.

type Domain

type Domain struct {
	Id            string             `json:"id"`
	Name          string             `json:"name"`
	DkimPublicKey string             `json:"dkimPublicKey"`
	DkimSelector  string             `json:"dkimSelector"`
	Status        string             `json:"status"`
	CreatedAt     string             `json:"createdAt"`
	VerifiedAt    string             `json:"verifiedAt"`
	Verification  DomainVerification `json:"verification"`
	Diagnostic    *DomainDiagnostic  `json:"diagnostic"`
}

Domain is the response from Domains.Get.

type DomainDiagnostic added in v0.11.1

type DomainDiagnostic struct {
	Code string `json:"code"`
	// FoundAt is the name the record was actually found at, for "doubled_domain".
	FoundAt string `json:"foundAt"`
	// PublishedChars and ExpectedChars describe a "truncated_key".
	PublishedChars int `json:"publishedChars"`
	ExpectedChars  int `json:"expectedChars"`
	// Target is where the CNAME points, for "cname_at_selector".
	Target   string             `json:"target"`
	Provider *DomainDnsProvider `json:"provider"`
}

DomainDiagnostic is what the last unmatched DNS check found, when it found a mistake rather than an absence. Nil while nothing is wrong beyond the records not having propagated yet.

Code is the mistake: "doubled_domain" (the record sits under the domain twice, because the control panel appends it to whatever you type), "truncated_key" (the value was cut at the 255-character limit for a single DNS string instead of being split into two), "foreign_key" (a DKIM key we did not issue is published at the selector), "quoted_value", "multiple_records", "cname_at_selector". New codes may be added, so treat an unknown one as generic.

type DomainDnsProvider added in v0.11.1

type DomainDnsProvider struct {
	Id    string `json:"id"`
	Label string `json:"label"`
	Guide string `json:"guide"`
}

DomainDnsProvider is the DNS host serving a domain's zone, recognised from its nameservers. Guide is a path on eusend.dev, empty where no guide exists.

type DomainListItem

type DomainListItem struct {
	Id        string `json:"id"`
	Name      string `json:"name"`
	Status    string `json:"status"`
	CreatedAt string `json:"createdAt"`
}

DomainListItem is a row from Domains.List.

type DomainVerification added in v0.7.1

type DomainVerification struct {
	Running   bool   `json:"running"`
	StartedAt string `json:"startedAt"`
}

DomainVerification reports whether a verification chain is polling DNS for the domain right now, and when that chain started.

type DomainsSvc

type DomainsSvc interface {
	Create(params *CreateDomainRequest) (*CreateDomainResponse, error)
	CreateWithContext(ctx context.Context, params *CreateDomainRequest) (*CreateDomainResponse, error)
	List() ([]DomainListItem, error)
	ListWithContext(ctx context.Context) ([]DomainListItem, error)
	Get(domainId string) (*Domain, error)
	GetWithContext(ctx context.Context, domainId string) (*Domain, error)
	Verify(domainId string) (*GenericResponse, error)
	VerifyWithContext(ctx context.Context, domainId string) (*GenericResponse, error)
	Remove(domainId string) (*GenericResponse, error)
	RemoveWithContext(ctx context.Context, domainId string) (*GenericResponse, error)
}

DomainsSvc is the /domains API.

type DomainsSvcImpl

type DomainsSvcImpl struct {
	// contains filtered or unexported fields
}

func (*DomainsSvcImpl) Create

func (*DomainsSvcImpl) CreateWithContext

func (s *DomainsSvcImpl) CreateWithContext(ctx context.Context, params *CreateDomainRequest) (*CreateDomainResponse, error)

func (*DomainsSvcImpl) Get

func (s *DomainsSvcImpl) Get(domainId string) (*Domain, error)

func (*DomainsSvcImpl) GetWithContext

func (s *DomainsSvcImpl) GetWithContext(ctx context.Context, domainId string) (*Domain, error)

func (*DomainsSvcImpl) List

func (s *DomainsSvcImpl) List() ([]DomainListItem, error)

func (*DomainsSvcImpl) ListWithContext

func (s *DomainsSvcImpl) ListWithContext(ctx context.Context) ([]DomainListItem, error)

func (*DomainsSvcImpl) Remove

func (s *DomainsSvcImpl) Remove(domainId string) (*GenericResponse, error)

func (*DomainsSvcImpl) RemoveWithContext

func (s *DomainsSvcImpl) RemoveWithContext(ctx context.Context, domainId string) (*GenericResponse, error)

func (*DomainsSvcImpl) Verify

func (s *DomainsSvcImpl) Verify(domainId string) (*GenericResponse, error)

func (*DomainsSvcImpl) VerifyWithContext

func (s *DomainsSvcImpl) VerifyWithContext(ctx context.Context, domainId string) (*GenericResponse, error)

type Email

type Email struct {
	Id          string            `json:"id"`
	From        string            `json:"from"`
	To          []string          `json:"to"`
	Cc          []string          `json:"cc"`
	Bcc         []string          `json:"bcc"`
	ReplyTo     []string          `json:"replyTo"`
	Subject     string            `json:"subject"`
	Html        string            `json:"html"`
	Text        string            `json:"text"`
	Status      string            `json:"status"`
	Tags        map[string]string `json:"tags"`
	TestMode    bool              `json:"testMode"`
	TemplateId  string            `json:"templateId"`
	ScheduledAt string            `json:"scheduledAt"`
	CreatedAt   string            `json:"createdAt"`
	Events      []EmailEvent      `json:"events"`
}

Email is the response from Emails.Get.

type EmailEvent

type EmailEvent struct {
	Id        string         `json:"id"`
	Type      string         `json:"type"`
	Metadata  map[string]any `json:"metadata"`
	CreatedAt string         `json:"createdAt"`
}

EmailEvent is one entry in an email's delivery timeline.

type EmailListItem

type EmailListItem struct {
	Id        string            `json:"id"`
	From      string            `json:"from"`
	To        []string          `json:"to"`
	Subject   string            `json:"subject"`
	Status    string            `json:"status"`
	Tags      map[string]string `json:"tags"`
	TestMode  bool              `json:"testMode"`
	CreatedAt string            `json:"createdAt"`
}

EmailListItem is a row from Emails.List.

type EmailsSvc

type EmailsSvc interface {
	Send(params *SendEmailRequest) (*SendEmailResponse, error)
	SendWithContext(ctx context.Context, params *SendEmailRequest) (*SendEmailResponse, error)
	SendWithOptions(ctx context.Context, params *SendEmailRequest, options *SendEmailOptions) (*SendEmailResponse, error)
	Get(emailId string) (*Email, error)
	GetWithContext(ctx context.Context, emailId string) (*Email, error)
	List(options *ListEmailsOptions) (*ListEmailsResponse, error)
	ListWithContext(ctx context.Context, options *ListEmailsOptions) (*ListEmailsResponse, error)
	Update(params *UpdateEmailRequest) (*UpdateEmailResponse, error)
	UpdateWithContext(ctx context.Context, params *UpdateEmailRequest) (*UpdateEmailResponse, error)
	Cancel(emailId string) (*CancelScheduledEmailResponse, error)
	CancelWithContext(ctx context.Context, emailId string) (*CancelScheduledEmailResponse, error)
}

EmailsSvc is the /emails API.

type EmailsSvcImpl

type EmailsSvcImpl struct {
	// contains filtered or unexported fields
}

func (*EmailsSvcImpl) Cancel

func (s *EmailsSvcImpl) Cancel(emailId string) (*CancelScheduledEmailResponse, error)

Cancel cancels a scheduled email. It fails once the email has started sending.

func (*EmailsSvcImpl) CancelWithContext

func (s *EmailsSvcImpl) CancelWithContext(ctx context.Context, emailId string) (*CancelScheduledEmailResponse, error)

func (*EmailsSvcImpl) Get

func (s *EmailsSvcImpl) Get(emailId string) (*Email, error)

Get retrieves an email by ID, including its delivery events.

func (*EmailsSvcImpl) GetWithContext

func (s *EmailsSvcImpl) GetWithContext(ctx context.Context, emailId string) (*Email, error)

func (*EmailsSvcImpl) List

List returns a page of emails, most recent first.

func (*EmailsSvcImpl) ListWithContext

func (s *EmailsSvcImpl) ListWithContext(ctx context.Context, options *ListEmailsOptions) (*ListEmailsResponse, error)

func (*EmailsSvcImpl) Send

Send sends a single email.

func (*EmailsSvcImpl) SendWithContext

func (s *EmailsSvcImpl) SendWithContext(ctx context.Context, params *SendEmailRequest) (*SendEmailResponse, error)

SendWithContext is Send with a caller-supplied context.

func (*EmailsSvcImpl) SendWithOptions

func (s *EmailsSvcImpl) SendWithOptions(ctx context.Context, params *SendEmailRequest, options *SendEmailOptions) (*SendEmailResponse, error)

SendWithOptions is Send with a context and per-call options (e.g. an idempotency key).

func (*EmailsSvcImpl) Update

Update reschedules a scheduled email. It fails once the email has started sending.

func (*EmailsSvcImpl) UpdateWithContext

func (s *EmailsSvcImpl) UpdateWithContext(ctx context.Context, params *UpdateEmailRequest) (*UpdateEmailResponse, error)

type Error

type Error struct {
	// Message is a human-readable description (the API's `error` field).
	Message string `json:"error"`
	// Code is a stable machine-readable code (the API's `code` field); see the
	// Code* constants. Branch on this rather than on Message.
	Code string `json:"code"`
	// StatusCode is the HTTP status, or 0 for a network-level failure
	// (Code == CodeApplicationError).
	StatusCode int `json:"-"`

	// RateLimit* are populated from response headers on a 429.
	RateLimitReset     string `json:"-"`
	RateLimitRemaining string `json:"-"`
	RetryAfter         string `json:"-"`
}

Error is returned when the API responds with a non-2xx status, or when the request never reaches the server. Inspect it with errors.As:

var apiErr *eusend.Error
if errors.As(err, &apiErr) && apiErr.Code == eusend.CodeMonthlyLimitExceeded {
    // back off and retry later
}

func (*Error) Error

func (e *Error) Error() string

type GenericResponse

type GenericResponse struct {
	Message string `json:"message"`
}

GenericResponse is a simple {"message": "..."} acknowledgement.

type ImportSuppressionsResponse added in v0.3.0

type ImportSuppressionsResponse struct {
	Count             int `json:"count"`
	AlreadySuppressed int `json:"already_suppressed"`
	Duplicates        int `json:"duplicates"`
}

ImportSuppressionsResponse reports what an import did. Count is what was written, AlreadySuppressed was on the list before, and Duplicates is how many rows the payload repeated — the three add up to the number of items sent.

type ListContactsOptions

type ListContactsOptions struct {
	Limit      int
	Cursor     string
	Search     string
	Subscribed *bool
}

ListContactsOptions filters Audiences.ListContacts. Zero-valued fields are omitted.

type ListContactsResponse

type ListContactsResponse struct {
	Data       []Contact `json:"data"`
	NextCursor string    `json:"next_cursor"`
}

ListContactsResponse is a page of contacts.

type ListEmailsOptions

type ListEmailsOptions struct {
	Limit  int
	Cursor string
	Status string
	From   string
	To     string
	// Tags filters by tag. "category:password_reset" matches that exact pair; a bare
	// "category" matches any email carrying the tag. Several entries are ANDed.
	Tags []string
}

ListEmailsOptions filters Emails.List. Zero-valued fields are omitted.

type ListEmailsResponse

type ListEmailsResponse struct {
	Data       []EmailListItem `json:"data"`
	NextCursor string          `json:"next_cursor"`
}

ListEmailsResponse is the response from Emails.List.

type ListSuppressionsOptions added in v0.3.0

type ListSuppressionsOptions struct {
	// Email matches addresses containing this substring. Pass a domain ("@acme.com")
	// to see every suppressed address there.
	Email  string
	Reason string
	Limit  int
	Cursor string
}

ListSuppressionsOptions filters Suppressions.List. Zero-valued fields are omitted.

type ListSuppressionsResponse added in v0.3.0

type ListSuppressionsResponse struct {
	Data       []SuppressionEntry `json:"data"`
	NextCursor string             `json:"next_cursor"`
}

ListSuppressionsResponse is a page of suppression entries, newest first.

type Options

type Options interface {
	GetIdempotencyKey() string
}

Options is implemented by per-call option structs (e.g. SendEmailOptions).

type RemoveSuppressionResponse added in v0.3.0

type RemoveSuppressionResponse struct {
	Deleted int `json:"deleted"`
}

RemoveSuppressionResponse reports how many entries were removed.

type SendBroadcastRequest

type SendBroadcastRequest struct {
	// ScheduledAt, when set, schedules the broadcast instead of sending immediately.
	ScheduledAt string `json:"scheduled_at,omitempty"`
}

SendBroadcastRequest sends or schedules a broadcast.

type SendBroadcastResponse

type SendBroadcastResponse struct {
	Id          string  `json:"id"`
	Status      string  `json:"status"`
	ScheduledAt *string `json:"scheduled_at"`
}

SendBroadcastResponse is the response from Broadcasts.Send.

type SendEmailOptions

type SendEmailOptions struct {
	// IdempotencyKey makes a send safe to retry: retrying with the same key
	// never sends a duplicate and returns the original email's ID.
	IdempotencyKey string `json:"-"`
}

SendEmailOptions carries per-call options for Emails.SendWithOptions.

func (SendEmailOptions) GetIdempotencyKey

func (o SendEmailOptions) GetIdempotencyKey() string

GetIdempotencyKey implements the Options interface.

type SendEmailRequest

type SendEmailRequest struct {
	// From accepts a bare email ("you@yourdomain.com") or a display-name form
	// ("Acme <you@yourdomain.com>"). The domain must be verified on your account.
	From       string            `json:"from"`
	To         []string          `json:"to"`
	Subject    string            `json:"subject,omitempty"`
	Bcc        []string          `json:"bcc,omitempty"`
	Cc         []string          `json:"cc,omitempty"`
	ReplyTo    []string          `json:"reply_to,omitempty"`
	Html       string            `json:"html,omitempty"`
	Text       string            `json:"text,omitempty"`
	TemplateId string            `json:"template_id,omitempty"`
	Variables  map[string]any    `json:"variables,omitempty"`
	Headers    map[string]string `json:"headers,omitempty"`
	// Tags label a send for log filtering and webhook routing, e.g.
	// {"category": "password_reset"}. Names and values may contain ASCII letters,
	// numbers, underscores and dashes; up to 10 tags per email. They are returned
	// on every email.* webhook event for this send.
	Tags        map[string]string `json:"tags,omitempty"`
	TrackOpens  *bool             `json:"track_opens,omitempty"`
	TrackClicks *bool             `json:"track_clicks,omitempty"`
	Attachments []*Attachment     `json:"attachments,omitempty"`
	// ScheduledAt schedules the send for a future time, at most 30 days out.
	// Accepts an ISO 8601 string or natural language ("in 1 hour", "tomorrow at
	// 9am"), parsed server-side in UTC. Not supported by Batch.Send.
	ScheduledAt string `json:"scheduled_at,omitempty"`
}

SendEmailRequest is the request object for Emails.Send. From and To are required; provide at least one of Html, Text, or TemplateId.

type SendEmailResponse

type SendEmailResponse struct {
	Id string `json:"id"`
}

SendEmailResponse is the response from Emails.Send.

type SuppressionEntry added in v0.3.0

type SuppressionEntry struct {
	Id        string `json:"id"`
	Email     string `json:"email"`
	Reason    string `json:"reason"`
	CreatedAt string `json:"created_at"`
}

SuppressionEntry is one address on the suppression list.

type SuppressionImportItem added in v0.3.0

type SuppressionImportItem struct {
	Email  string `json:"email"`
	Reason string `json:"reason,omitempty"`
}

SuppressionImportItem is one entry in an import. Reason is optional and defaults to "manual".

type SuppressionsSvc added in v0.3.0

type SuppressionsSvc interface {
	List(options *ListSuppressionsOptions) (*ListSuppressionsResponse, error)
	ListWithContext(ctx context.Context, options *ListSuppressionsOptions) (*ListSuppressionsResponse, error)
	Create(params *CreateSuppressionRequest) (*SuppressionEntry, error)
	CreateWithContext(ctx context.Context, params *CreateSuppressionRequest) (*SuppressionEntry, error)
	Import(items []*SuppressionImportItem) (*ImportSuppressionsResponse, error)
	ImportWithContext(ctx context.Context, items []*SuppressionImportItem) (*ImportSuppressionsResponse, error)
	Remove(idOrEmail string) (*RemoveSuppressionResponse, error)
	RemoveWithContext(ctx context.Context, idOrEmail string) (*RemoveSuppressionResponse, error)
	Export() ([]byte, error)
	ExportWithContext(ctx context.Context) ([]byte, error)
}

SuppressionsSvc is the /suppressions API — the addresses your organization will not send to. Hard bounces and spam complaints are added automatically; these methods cover the ones you manage yourself. Suppression applies to live sending only, so test-mode keys can read the list but not modify it.

type SuppressionsSvcImpl added in v0.3.0

type SuppressionsSvcImpl struct {
	// contains filtered or unexported fields
}

func (*SuppressionsSvcImpl) Create added in v0.3.0

func (*SuppressionsSvcImpl) CreateWithContext added in v0.3.0

func (s *SuppressionsSvcImpl) CreateWithContext(ctx context.Context, params *CreateSuppressionRequest) (*SuppressionEntry, error)

func (*SuppressionsSvcImpl) Export added in v0.3.0

func (s *SuppressionsSvcImpl) Export() ([]byte, error)

func (*SuppressionsSvcImpl) ExportWithContext added in v0.3.0

func (s *SuppressionsSvcImpl) ExportWithContext(ctx context.Context) ([]byte, error)

ExportWithContext returns the whole list as CSV ("email,reason,created_at"), for backup or migration. Unlike every other method here the body is not JSON, so it comes back as raw bytes.

func (*SuppressionsSvcImpl) Import added in v0.3.0

func (*SuppressionsSvcImpl) ImportWithContext added in v0.3.0

ImportWithContext adds up to 1000 addresses in one call — for carrying a suppression list over from another provider before your first send.

func (*SuppressionsSvcImpl) List added in v0.3.0

func (*SuppressionsSvcImpl) ListWithContext added in v0.3.0

func (*SuppressionsSvcImpl) Remove added in v0.3.0

func (s *SuppressionsSvcImpl) Remove(idOrEmail string) (*RemoveSuppressionResponse, error)

func (*SuppressionsSvcImpl) RemoveWithContext added in v0.3.0

func (s *SuppressionsSvcImpl) RemoveWithContext(ctx context.Context, idOrEmail string) (*RemoveSuppressionResponse, error)

RemoveWithContext un-suppresses by entry id or by address, making the address sendable again. Removing addresses that hard-bounced or complained is what damages a sender's reputation when done in bulk.

type Template

type Template struct {
	Id          string `json:"id"`
	Name        string `json:"name"`
	Subject     string `json:"subject"`
	Html        string `json:"html"`
	ReactSource string `json:"reactSource"`
	CreatedAt   string `json:"createdAt"`
	UpdatedAt   string `json:"updatedAt"`
}

Template is a saved email template.

type TemplateListItem

type TemplateListItem struct {
	Id        string `json:"id"`
	Name      string `json:"name"`
	Subject   string `json:"subject"`
	CreatedAt string `json:"createdAt"`
	UpdatedAt string `json:"updatedAt"`
}

TemplateListItem is a row from Templates.List.

type TemplatesSvc

type TemplatesSvc interface {
	Create(params *CreateTemplateRequest) (*Template, error)
	CreateWithContext(ctx context.Context, params *CreateTemplateRequest) (*Template, error)
	List() ([]TemplateListItem, error)
	ListWithContext(ctx context.Context) ([]TemplateListItem, error)
	Get(templateId string) (*Template, error)
	GetWithContext(ctx context.Context, templateId string) (*Template, error)
	Update(templateId string, params *UpdateTemplateRequest) (*Template, error)
	UpdateWithContext(ctx context.Context, templateId string, params *UpdateTemplateRequest) (*Template, error)
	Remove(templateId string) (*GenericResponse, error)
	RemoveWithContext(ctx context.Context, templateId string) (*GenericResponse, error)
}

TemplatesSvc is the /templates API.

type TemplatesSvcImpl

type TemplatesSvcImpl struct {
	// contains filtered or unexported fields
}

func (*TemplatesSvcImpl) Create

func (s *TemplatesSvcImpl) Create(params *CreateTemplateRequest) (*Template, error)

func (*TemplatesSvcImpl) CreateWithContext

func (s *TemplatesSvcImpl) CreateWithContext(ctx context.Context, params *CreateTemplateRequest) (*Template, error)

func (*TemplatesSvcImpl) Get

func (s *TemplatesSvcImpl) Get(templateId string) (*Template, error)

func (*TemplatesSvcImpl) GetWithContext

func (s *TemplatesSvcImpl) GetWithContext(ctx context.Context, templateId string) (*Template, error)

func (*TemplatesSvcImpl) List

func (s *TemplatesSvcImpl) List() ([]TemplateListItem, error)

func (*TemplatesSvcImpl) ListWithContext

func (s *TemplatesSvcImpl) ListWithContext(ctx context.Context) ([]TemplateListItem, error)

func (*TemplatesSvcImpl) Remove

func (s *TemplatesSvcImpl) Remove(templateId string) (*GenericResponse, error)

func (*TemplatesSvcImpl) RemoveWithContext

func (s *TemplatesSvcImpl) RemoveWithContext(ctx context.Context, templateId string) (*GenericResponse, error)

func (*TemplatesSvcImpl) Update

func (s *TemplatesSvcImpl) Update(templateId string, params *UpdateTemplateRequest) (*Template, error)

func (*TemplatesSvcImpl) UpdateWithContext

func (s *TemplatesSvcImpl) UpdateWithContext(ctx context.Context, templateId string, params *UpdateTemplateRequest) (*Template, error)

type TestBroadcastRequest added in v0.8.0

type TestBroadcastRequest struct {
	// To holds up to 5 addresses, each on a domain verified on your account. A test send
	// delivers real mail without the paid-plan gate Send carries, so it is restricted to
	// inboxes you have already proved you control; anything else returns
	// DOMAIN_NOT_VERIFIED.
	To []string `json:"to"`
}

TestBroadcastRequest is the payload for Broadcasts.Test.

type TestBroadcastResponse added in v0.8.0

type TestBroadcastResponse struct {
	Id string `json:"id"`
	// SentTo lists the addresses actually mailed, lowercased and de-duplicated.
	SentTo []string `json:"sent_to"`
	// EmailIds holds one email id per recipient, for looking the delivery up in the logs.
	EmailIds []string `json:"email_ids"`
}

TestBroadcastResponse is the response from Broadcasts.Test.

type UpdateBroadcastRequest

type UpdateBroadcastRequest struct {
	Name              *string           `json:"name,omitempty"`
	AudienceId        *string           `json:"audience_id,omitempty"`
	From              *string           `json:"from,omitempty"`
	Subject           *string           `json:"subject,omitempty"`
	Html              *string           `json:"html,omitempty"`
	TemplateId        *string           `json:"template_id,omitempty"`
	TemplateVariables map[string]string `json:"template_variables,omitempty"`
	ScheduledAt       *string           `json:"scheduled_at,omitempty"`
	// Nil leaves the broadcast's current setting unchanged.
	TrackOpens  *bool `json:"track_opens,omitempty"`
	TrackClicks *bool `json:"track_clicks,omitempty"`
}

UpdateBroadcastRequest updates a broadcast. Empty/nil fields are left unchanged.

type UpdateContactRequest

type UpdateContactRequest struct {
	FirstName    *string `json:"first_name,omitempty"`
	LastName     *string `json:"last_name,omitempty"`
	Unsubscribed *bool   `json:"unsubscribed,omitempty"`
	// Properties REPLACES the contact's custom properties. Nil leaves them unchanged;
	// an empty (non-nil) map clears them.
	Properties map[string]string `json:"properties,omitempty"`
}

UpdateContactRequest updates a contact. Nil fields are left unchanged.

type UpdateEmailRequest

type UpdateEmailRequest struct {
	Id          string `json:"-"`
	ScheduledAt string `json:"scheduled_at"`
}

UpdateEmailRequest reschedules a scheduled email.

type UpdateEmailResponse

type UpdateEmailResponse struct {
	Id          string `json:"id"`
	Status      string `json:"status"`
	ScheduledAt string `json:"scheduled_at"`
}

UpdateEmailResponse is the response from Emails.Update.

type UpdateTemplateRequest

type UpdateTemplateRequest struct {
	Name    *string `json:"name,omitempty"`
	Subject *string `json:"subject,omitempty"`
	Html    *string `json:"html,omitempty"`
}

UpdateTemplateRequest updates a template. Nil fields are left unchanged.

type UpdateWebhookRequest

type UpdateWebhookRequest struct {
	Url    *string  `json:"url,omitempty"`
	Events []string `json:"events,omitempty"`
}

UpdateWebhookRequest updates a webhook. Nil/empty fields are left unchanged.

type Webhook

type Webhook struct {
	Id         string            `json:"id"`
	Url        string            `json:"url"`
	Events     []string          `json:"events"`
	Secret     string            `json:"secret,omitempty"`
	CreatedAt  string            `json:"createdAt"`
	Deliveries []WebhookDelivery `json:"deliveries,omitempty"`
}

Webhook is a webhook subscription. Secret is populated only by Create; Deliveries only by Get.

type WebhookDelivery

type WebhookDelivery struct {
	Id             string         `json:"id"`
	WebhookId      string         `json:"webhookId"`
	EmailId        string         `json:"emailId"`
	EventType      string         `json:"eventType"`
	Payload        map[string]any `json:"payload"`
	Status         string         `json:"status"`
	ResponseStatus int            `json:"responseStatus"`
	Attempts       int            `json:"attempts"`
	CreatedAt      string         `json:"createdAt"`
	LastAttemptAt  string         `json:"lastAttemptAt"`
}

WebhookDelivery is one delivery attempt of a webhook event.

type WebhooksSvc

type WebhooksSvc interface {
	Create(params *CreateWebhookRequest) (*Webhook, error)
	CreateWithContext(ctx context.Context, params *CreateWebhookRequest) (*Webhook, error)
	List() ([]Webhook, error)
	ListWithContext(ctx context.Context) ([]Webhook, error)
	Get(webhookId string) (*Webhook, error)
	GetWithContext(ctx context.Context, webhookId string) (*Webhook, error)
	Update(webhookId string, params *UpdateWebhookRequest) (*Webhook, error)
	UpdateWithContext(ctx context.Context, webhookId string, params *UpdateWebhookRequest) (*Webhook, error)
	Remove(webhookId string) (*GenericResponse, error)
	RemoveWithContext(ctx context.Context, webhookId string) (*GenericResponse, error)
}

WebhooksSvc is the /webhooks API.

type WebhooksSvcImpl

type WebhooksSvcImpl struct {
	// contains filtered or unexported fields
}

func (*WebhooksSvcImpl) Create

func (s *WebhooksSvcImpl) Create(params *CreateWebhookRequest) (*Webhook, error)

func (*WebhooksSvcImpl) CreateWithContext

func (s *WebhooksSvcImpl) CreateWithContext(ctx context.Context, params *CreateWebhookRequest) (*Webhook, error)

func (*WebhooksSvcImpl) Get

func (s *WebhooksSvcImpl) Get(webhookId string) (*Webhook, error)

func (*WebhooksSvcImpl) GetWithContext

func (s *WebhooksSvcImpl) GetWithContext(ctx context.Context, webhookId string) (*Webhook, error)

func (*WebhooksSvcImpl) List

func (s *WebhooksSvcImpl) List() ([]Webhook, error)

func (*WebhooksSvcImpl) ListWithContext

func (s *WebhooksSvcImpl) ListWithContext(ctx context.Context) ([]Webhook, error)

func (*WebhooksSvcImpl) Remove

func (s *WebhooksSvcImpl) Remove(webhookId string) (*GenericResponse, error)

func (*WebhooksSvcImpl) RemoveWithContext

func (s *WebhooksSvcImpl) RemoveWithContext(ctx context.Context, webhookId string) (*GenericResponse, error)

func (*WebhooksSvcImpl) Update

func (s *WebhooksSvcImpl) Update(webhookId string, params *UpdateWebhookRequest) (*Webhook, error)

func (*WebhooksSvcImpl) UpdateWithContext

func (s *WebhooksSvcImpl) UpdateWithContext(ctx context.Context, webhookId string, params *UpdateWebhookRequest) (*Webhook, error)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL