sendara

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Jun 15, 2026 License: MIT Imports: 19 Imported by: 0

README

Sendara Go SDK

Official Go client for the Sendara messaging API — email, SMS, broadcasts, contacts, templates, domains, webhooks, and billing.

Zero non-stdlib dependencies. Typed request/response structs, a typed error type with classification helpers, automatic retries with backoff (honoring Retry-After), idempotency-key auto-generation, a cursor-based message iterator, multipart image uploads, and an outbound-webhook signature verifier.

Install

go get github.com/sendaramail/sendara-go@v0.2.0
import sendara "github.com/sendaramail/sendara-go"

Quickstart

package main

import (
	"context"
	"fmt"
	"log"

	sendara "github.com/sendaramail/sendara-go"
)

func main() {
	client := sendara.New("sk_live_...")

	resp, err := client.Emails.Send(context.Background(), sendara.EmailSendParams{
		From:    "hello@yourdomain.com",
		To:      "user@example.com",
		Subject: "Welcome",
		HTML:    "<h1>Hi there</h1>",
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("queued:", resp.ID)
}

Configuration

The client takes functional options:

client := sendara.New(
	"sk_live_...",
	sendara.WithBaseURL("https://api.sendara.dev"),
	sendara.WithTimeout(15 * time.Second),
	sendara.WithMaxRetries(5),
	sendara.WithHTTPClient(myHTTPClient),
)
Option Description
WithBaseURL(url) Override the API base URL.
WithTimeout(d) Per-request timeout (default 30s; no-op with a custom HTTP client).
WithMaxRetries(n) Bound automatic retries (default 3; 0 disables).
WithHTTPClient(c) Inject a custom HTTP client / transport.
WithUserAgent(s) Append a token to the SDK User-Agent.

Errors

Every non-2xx response returns a *sendara.Error exposing Status, Code, Message, and RetryAfter. Branch on the failure class with helpers:

resp, err := client.Emails.Send(ctx, params)
if err != nil {
	var apiErr *sendara.Error
	if errors.As(err, &apiErr) {
		switch {
		case apiErr.IsRateLimited():
			time.Sleep(apiErr.RetryAfter)
		case apiErr.IsAuthentication():
			log.Fatal("bad API key")
		case apiErr.IsValidation():
			log.Printf("invalid request: %s", apiErr.Message)
		}
	}
}

Classifiers: IsAuthentication (401), IsPermission (403), IsNotFound (404), IsConflict (409), IsValidation (400/422), IsRateLimited (429), IsServer (5xx), IsTransport (network error).

Retries & idempotency

GETs and idempotent calls (sends, deletes, rotations, PUT updates) are retried automatically on 429, 5xx, and transport errors, with exponential backoff + full jitter, honoring a server Retry-After. Non-idempotent POSTs (create contact/list/template/etc.) are never retried.

Sends accept an IdempotencyKey; when omitted the SDK auto-generates a UUID so the call can be retried safely. Override it to dedupe across process restarts.

Pagination

Messages.Iterator transparently follows the opaque cursor:

it := client.Messages.Iterator(sendara.ListMessagesParams{Channel: "email"})
for it.Next(ctx) {
	fmt.Println(it.Message().ID)
}
if err := it.Err(); err != nil {
	log.Fatal(err)
}

Or grab a single page with Messages.List, or collect everything with it.All(ctx).

Webhook verification

Verify the signature of inbound webhook deliveries (HMAC-SHA256 of "<timestamp>.<rawBody>", hex-encoded):

func handler(w http.ResponseWriter, r *http.Request) {
	body, _ := io.ReadAll(r.Body)
	event, err := sendara.VerifyWebhookRequest(
		webhookSigningSecret, r.Header, body, 5*time.Minute,
	)
	if err != nil {
		http.Error(w, "invalid signature", http.StatusForbidden)
		return
	}
	fmt.Println("event:", event.EventType, event.MessageID)
	w.WriteHeader(http.StatusOK)
}

VerifyWebhook(secret, timestamp, signature, body, tolerance) is the lower-level primitive. Pass tolerance == 0 to skip the replay-window check.

API coverage

Namespace Methods
client.Emails Send
client.SMS Send
client.Send Send, Batch
client.Broadcasts Create, List, Get, Send, Cancel, Delete, BulkSend
client.Messages List, Get, Iterator (+ Next/Message/Err/All)
client.Suppressions List, Create, Delete
client.Domains List, Create, Get, Verify
client.APIKeys List, Create, Rotate, Revoke
client.Usage Get, SetSpendCap
client.Billing Get, Checkout, Portal
client.Templates Create, List, Get, Update, Delete, Render
client.Contacts Create, List, Get, Update, Delete, Import
client.Contacts.Lists Create, List, Get, Update, Delete, AddMember, RemoveMember, Members
client.Webhooks Create, List, Get, Update, Delete, Deliveries, RotateSecret
client.Uploads Upload, UploadBytes
client.TestRecipients List, Create, Resend, Delete

Package-level helpers: VerifyWebhook, VerifyWebhookRequest, and the optional pointer constructors String, Bool, Int64, NullableString for building update params.

Development

cd sdk/go
go build ./...
go test ./...

License

MIT

Documentation

Overview

Package sendara is the official Go SDK for the Sendara messaging API (https://sendara.dev). It provides a typed client for sending email and SMS, managing broadcasts, contacts, templates, domains, API keys, suppressions, webhooks, and billing, with automatic retries, idempotency, cursor-based pagination, and an outbound-webhook signature verifier.

Quickstart:

client := sendara.New("sk_live_...")
resp, err := client.Emails.Send(ctx, sendara.EmailSendParams{
    From:    "hello@yourdomain.com",
    To:      "user@example.com",
    Subject: "Welcome",
    HTML:    "<h1>Hi there</h1>",
})

Index

Constants

View Source
const (
	HeaderSignature = "Sendara-Signature"
	HeaderTimestamp = "Sendara-Timestamp"
	HeaderEventID   = "Sendara-Event-Id"
	HeaderEventType = "Sendara-Event-Type"
)

Webhook signature header names sent on every outbound delivery.

View Source
const DefaultBaseURL = "https://api.sendara.dev"

DefaultBaseURL is the production API base URL.

View Source
const Version = "0.2.1"

Version is the released version of the Sendara Go SDK. It is sent in the User-Agent header and intended to be published as the git tag go-v<Version>.

Variables

View Source
var (
	ErrMissingSignature = errors.New("sendara: missing Sendara-Signature header")
	ErrMissingTimestamp = errors.New("sendara: missing Sendara-Timestamp header")
	ErrInvalidSignature = errors.New("sendara: webhook signature verification failed")
	ErrTimestampTooOld  = errors.New("sendara: webhook timestamp outside tolerance window")
	ErrInvalidTimestamp = errors.New("sendara: invalid Sendara-Timestamp header")
)

Errors returned by webhook verification.

View Source
var ErrMissingAPIKey = errors.New("sendara: an API key is required")

ErrMissingAPIKey is returned by New when no API key is supplied.

Functions

func Bool

func Bool(v bool) *bool

Bool returns a pointer to v, for optional boolean request fields.

func Int64

func Int64(v int64) *int64

Int64 returns a pointer to v, for optional numeric request fields.

func NullableString

func NullableString(s *string) **string

NullableString returns a **string for UpdateContactParams fields, letting the caller distinguish "leave unchanged" from "set to null". Pass a nil *string to explicitly clear the field.

func String

func String(s string) *string

String returns a pointer to s, a convenience for optional string fields in update params (e.g. UpdateTemplateParams.Name).

func VerifyWebhook

func VerifyWebhook(secret, timestamp, signature string, body []byte, tolerance time.Duration) error

VerifyWebhook checks that body was signed with secret for the given timestamp and signature header values, using constant-time comparison. It returns nil when the signature is valid. tolerance, when > 0, additionally rejects timestamps that are too far from now (replay protection); pass 0 to skip the timestamp window check.

Types

type APIKey

type APIKey struct {
	ID           string     `json:"id"`
	AccountID    string     `json:"account_id"`
	KeyPrefix    string     `json:"key_prefix"`
	Scope        string     `json:"scope"`
	Name         string     `json:"name"`
	IsRevoked    bool       `json:"is_revoked"`
	TestMode     bool       `json:"test_mode"`
	LastUsedAt   *time.Time `json:"last_used_at"`
	RequestCount int64      `json:"request_count"`
	CreatedAt    time.Time  `json:"created_at"`
}

APIKey is an API key (without the secret).

type APIKeysService

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

APIKeysService manages API keys (/v1/keys). Key management requires an admin-scoped key or a dashboard session.

func (*APIKeysService) Create

Create issues a new API key. The plaintext key in the result is returned exactly once. An empty Scope defaults to a send-scoped production key.

func (*APIKeysService) List

func (s *APIKeysService) List(ctx context.Context) ([]APIKey, error)

List returns the account's API keys (without secrets).

func (*APIKeysService) Revoke

func (s *APIKeysService) Revoke(ctx context.Context, id string) error

Revoke permanently disables a key.

func (*APIKeysService) Rotate

func (s *APIKeysService) Rotate(ctx context.Context, id string) (string, error)

Rotate revokes a key and issues a replacement with the same scope, returning the new plaintext key.

type BatchItemError

type BatchItemError struct {
	Code    string `json:"code"`
	Message string `json:"message"`
	Status  int    `json:"status"`
}

BatchItemError is the per-item error detail in a batch result.

type BatchItemResult

type BatchItemResult struct {
	Success  bool            `json:"success"`
	Response *SendResponse   `json:"response,omitempty"`
	Error    *BatchItemError `json:"error,omitempty"`
}

BatchItemResult is the outcome of one item in a batch send.

type BillingService

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

BillingService reads plan state and opens checkout/portal sessions (/v1/billing).

func (*BillingService) Checkout

func (s *BillingService) Checkout(ctx context.Context, plan string) (string, error)

Checkout starts a subscription and returns the hosted checkout URL. An empty plan defaults to "pro"; pass "starter" for the starter plan.

func (*BillingService) Get

Get returns the account's current plan and subscription status.

func (*BillingService) Portal

func (s *BillingService) Portal(ctx context.Context) (string, error)

Portal returns a customer-portal URL for managing the subscription.

type BillingState

type BillingState struct {
	Plan               string `json:"plan"`
	SubscriptionStatus string `json:"subscription_status"`
}

BillingState is the account's current plan and subscription status.

type BimiDMARC

type BimiDMARC struct {
	AtEnforcement bool   `json:"at_enforcement"`
	Recommended   string `json:"recommended"`
}

BimiDMARC describes the DMARC posture required for BIMI to take effect.

type BimiRecord

type BimiRecord struct {
	Type  string `json:"type"`
	Name  string `json:"name"`
	Value string `json:"value"`
}

BimiRecord is the BIMI DNS TXT record a customer must publish.

type BimiStatus

type BimiStatus struct {
	LogoURL       *string     `json:"logo_url"`
	Record        *BimiRecord `json:"record"`
	DMARC         BimiDMARC   `json:"dmarc"`
	BimiPublished bool        `json:"bimi_published"`
	VMCNote       string      `json:"vmc_note"`
}

BimiStatus is the BIMI configuration and readiness for a sending domain.

type Broadcast

type Broadcast struct {
	ID              string      `json:"id"`
	AccountID       string      `json:"account_id"`
	Name            string      `json:"name"`
	Status          string      `json:"status"`
	FromEmail       string      `json:"from_email"`
	Subject         *string     `json:"subject,omitempty"`
	BodyHTML        *string     `json:"body_html,omitempty"`
	BodyText        *string     `json:"body_text,omitempty"`
	TemplateID      *string     `json:"template_id,omitempty"`
	MessageType     string      `json:"message_type"`
	AudienceListID  *string     `json:"audience_list_id,omitempty"`
	Recipients      []Recipient `json:"recipients,omitempty"`
	ScheduledAt     *time.Time  `json:"scheduled_at,omitempty"`
	TotalRecipients int         `json:"total_recipients"`
	SentCount       int         `json:"sent_count"`
	FailedCount     int         `json:"failed_count"`
	TestMode        bool        `json:"test_mode"`
	Error           *string     `json:"error,omitempty"`
	StartedAt       *time.Time  `json:"started_at,omitempty"`
	CompletedAt     *time.Time  `json:"completed_at,omitempty"`
	CreatedAt       time.Time   `json:"created_at"`
	UpdatedAt       time.Time   `json:"updated_at"`
}

Broadcast is a bulk email campaign.

type BroadcastDetail

type BroadcastDetail struct {
	Broadcast *Broadcast      `json:"broadcast"`
	Stats     *BroadcastStats `json:"stats"`
}

BroadcastDetail wraps a broadcast with its aggregate delivery stats, as returned by GET /v1/broadcasts/{id}.

type BroadcastStats

type BroadcastStats struct {
	Total      int `json:"total"`
	Queued     int `json:"queued"`
	Sent       int `json:"sent"`
	Delivered  int `json:"delivered"`
	Bounced    int `json:"bounced"`
	Complained int `json:"complained"`
	Opened     int `json:"opened"`
	Failed     int `json:"failed"`
}

BroadcastStats are aggregate delivery metrics for a broadcast.

type BroadcastsService

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

BroadcastsService manages bulk email campaigns (/v1/broadcasts and /v1/send/bulk).

func (*BroadcastsService) BulkSend

BulkSend creates and immediately fans out a broadcast in one call (POST /v1/send/bulk), returning the broadcast to poll for progress.

func (*BroadcastsService) Cancel

func (s *BroadcastsService) Cancel(ctx context.Context, id string) (*Broadcast, error)

Cancel cancels a draft or scheduled broadcast.

func (*BroadcastsService) Create

Create creates a broadcast. Set SendNow to fan it out immediately.

func (*BroadcastsService) Delete

func (s *BroadcastsService) Delete(ctx context.Context, id string) error

Delete removes a broadcast.

func (*BroadcastsService) Get

Get returns a broadcast with its aggregate delivery stats.

func (*BroadcastsService) List

List returns the account's broadcasts.

func (*BroadcastsService) Send

func (s *BroadcastsService) Send(ctx context.Context, id string) (*Broadcast, error)

Send fans out a draft or scheduled broadcast now.

type ChannelUsage

type ChannelUsage struct {
	Channel    string `json:"channel"`
	SendCount  int64  `json:"send_count"`
	CostMicros int64  `json:"cost_micros"`
}

ChannelUsage is per-channel usage within a billing period.

type Client

type Client struct {

	// Resource namespaces.
	Emails         *EmailsService
	SMS            *SMSService
	Send           *SendService
	Broadcasts     *BroadcastsService
	Messages       *MessagesService
	Suppressions   *SuppressionsService
	Domains        *DomainsService
	APIKeys        *APIKeysService
	Usage          *UsageService
	Billing        *BillingService
	Templates      *TemplatesService
	Contacts       *ContactsService
	Webhooks       *WebhooksService
	Uploads        *UploadsService
	TestRecipients *TestRecipientsService
	// contains filtered or unexported fields
}

Client is the Sendara API client. Construct it with New and configure it with functional Options. It is safe for concurrent use by multiple goroutines.

func New

func New(apiKey string, opts ...Option) *Client

New constructs a Client. The apiKey is required (sk_live_... or sk_test_...). It panics only if apiKey is empty — callers that prefer an error should check the key before calling.

type Contact

type Contact struct {
	ID           string         `json:"id"`
	AccountID    string         `json:"account_id"`
	Email        *string        `json:"email"`
	PhoneNumber  *string        `json:"phone_number"`
	DeviceToken  *string        `json:"device_token"`
	FirstName    string         `json:"first_name"`
	LastName     string         `json:"last_name"`
	Attributes   map[string]any `json:"attributes"`
	Tags         []string       `json:"tags"`
	EmailConsent string         `json:"email_consent"`
	SMSConsent   string         `json:"sms_consent"`
	PushConsent  string         `json:"push_consent"`
	VoiceConsent string         `json:"voice_consent"`
	CreatedAt    time.Time      `json:"created_at"`
	UpdatedAt    time.Time      `json:"updated_at"`
}

Contact is a stored audience contact.

type ContactList

type ContactList struct {
	ID           string        `json:"id"`
	AccountID    string        `json:"account_id"`
	Name         string        `json:"name"`
	ListType     string        `json:"list_type"`
	SegmentRules *SegmentRules `json:"segment_rules,omitempty"`
	CreatedAt    time.Time     `json:"created_at"`
	UpdatedAt    time.Time     `json:"updated_at"`
}

ContactList is an audience list (static or dynamic).

type ContactListMember

type ContactListMember struct {
	ID            string    `json:"id"`
	ContactListID string    `json:"contact_list_id"`
	ContactID     string    `json:"contact_id"`
	AddedAt       time.Time `json:"added_at"`
}

ContactListMember is a membership row linking a contact to a static list.

type ContactsService

type ContactsService struct {
	Lists *ListsService
	// contains filtered or unexported fields
}

ContactsService manages audience contacts and lists (/v1/contacts).

func (*ContactsService) Create

func (s *ContactsService) Create(ctx context.Context, params CreateContactParams) (*Contact, error)

Create creates a contact.

func (*ContactsService) Delete

func (s *ContactsService) Delete(ctx context.Context, id string) error

Delete removes a contact.

func (*ContactsService) Get

func (s *ContactsService) Get(ctx context.Context, id string) (*Contact, error)

Get returns a single contact.

func (*ContactsService) Import

Import imports contacts from a staged S3 object (csv or json).

func (*ContactsService) List

func (s *ContactsService) List(ctx context.Context) ([]Contact, error)

List returns the account's contacts.

func (*ContactsService) Update

func (s *ContactsService) Update(ctx context.Context, id string, params UpdateContactParams) (*Contact, error)

Update patches a contact.

type CreateAPIKeyParams

type CreateAPIKeyParams struct {
	Scope    string `json:"scope,omitempty"`
	TestMode bool   `json:"test_mode,omitempty"`
}

CreateAPIKeyParams configures a new API key.

type CreateBroadcastParams

type CreateBroadcastParams struct {
	Name           string      `json:"name,omitempty"`
	FromEmail      string      `json:"from_email"`
	Subject        *string     `json:"subject,omitempty"`
	BodyHTML       *string     `json:"body_html,omitempty"`
	BodyText       *string     `json:"body_text,omitempty"`
	TemplateID     *string     `json:"template_id,omitempty"`
	MessageType    string      `json:"message_type,omitempty"`
	AudienceListID *string     `json:"audience_list_id,omitempty"`
	Recipients     []Recipient `json:"recipients,omitempty"`
	ScheduledAt    *time.Time  `json:"scheduled_at,omitempty"`
	SendNow        bool        `json:"send_now,omitempty"`
}

CreateBroadcastParams creates a broadcast.

type CreateContactParams

type CreateContactParams struct {
	Email        *string        `json:"email"`
	PhoneNumber  *string        `json:"phone_number"`
	DeviceToken  *string        `json:"device_token"`
	FirstName    string         `json:"first_name,omitempty"`
	LastName     string         `json:"last_name,omitempty"`
	Attributes   map[string]any `json:"attributes,omitempty"`
	Tags         []string       `json:"tags,omitempty"`
	EmailConsent string         `json:"email_consent,omitempty"`
	SMSConsent   string         `json:"sms_consent,omitempty"`
	PushConsent  string         `json:"push_consent,omitempty"`
	VoiceConsent string         `json:"voice_consent,omitempty"`
}

CreateContactParams creates a contact.

type CreateListParams

type CreateListParams struct {
	Name         string        `json:"name"`
	ListType     string        `json:"list_type"`
	SegmentRules *SegmentRules `json:"segment_rules,omitempty"`
}

CreateListParams creates a contact list.

type CreateSuppressionParams

type CreateSuppressionParams struct {
	Channel   string `json:"channel"`
	Recipient string `json:"recipient"`
	Reason    string `json:"reason,omitempty"`
}

CreateSuppressionParams adds a recipient to the suppression list.

type CreateTemplateParams

type CreateTemplateParams struct {
	Name      string             `json:"name"`
	Channel   string             `json:"channel"`
	Subject   *string            `json:"subject,omitempty"`
	BodyText  *string            `json:"body_text,omitempty"`
	BodyHTML  *string            `json:"body_html,omitempty"`
	BodyJSON  *json.RawMessage   `json:"body_json,omitempty"`
	Variables []TemplateVariable `json:"variables,omitempty"`
}

CreateTemplateParams creates a template.

type CreateWebhookParams

type CreateWebhookParams struct {
	EndpointURL string   `json:"endpoint_url"`
	EventTypes  []string `json:"event_types,omitempty"`
}

CreateWebhookParams creates a webhook subscription.

type CreatedAPIKey

type CreatedAPIKey struct {
	ID        string `json:"id"`
	Key       string `json:"key"`
	KeyPrefix string `json:"key_prefix"`
	Scope     string `json:"scope"`
	TestMode  bool   `json:"test_mode"`
	CreatedAt string `json:"created_at"`
}

CreatedAPIKey is returned once on creation and includes the plaintext key.

type DNSRecord

type DNSRecord struct {
	Type  string `json:"type"`
	Name  string `json:"name"`
	Value string `json:"value"`
}

DNSRecord is a DNS record the customer must configure for a sending domain.

type Domain

type Domain struct {
	ID             string      `json:"id"`
	AccountID      string      `json:"account_id"`
	Domain         string      `json:"domain"`
	DKIMStatus     string      `json:"dkim_status"`
	SPFStatus      string      `json:"spf_status"`
	DMARCStatus    string      `json:"dmarc_status"`
	TXTStatus      string      `json:"txt_status"`
	DNSRecords     []DNSRecord `json:"dns_records"`
	IsSandbox      bool        `json:"is_sandbox"`
	MailFromDomain string      `json:"mail_from_domain,omitempty"`
	CreatedAt      time.Time   `json:"created_at"`
	UpdatedAt      time.Time   `json:"updated_at"`
}

Domain is a sending domain and its verification status.

type DomainVerification

type DomainVerification struct {
	Domain        string         `json:"domain"`
	FullyVerified bool           `json:"fully_verified"`
	Results       []RecordResult `json:"results"`
}

DomainVerification is the result of POST /v1/domains/{domain}/verify.

type DomainsService

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

DomainsService manages sending domains (/v1/domains).

func (*DomainsService) Create

func (s *DomainsService) Create(ctx context.Context, domain string) (*Domain, error)

Create registers a new sending domain and returns the DNS records to set.

func (*DomainsService) Get

func (s *DomainsService) Get(ctx context.Context, domain string) (*Domain, error)

Get returns a single domain with its DNS records and verification status.

func (*DomainsService) GetBimi

func (s *DomainsService) GetBimi(ctx context.Context, domain string) (*BimiStatus, error)

GetBimi returns the BIMI configuration and readiness for a domain.

func (*DomainsService) List

func (s *DomainsService) List(ctx context.Context) ([]Domain, error)

List returns the account's sending domains.

func (*DomainsService) SetBimi

func (s *DomainsService) SetBimi(ctx context.Context, domain, logoURL string) (*BimiStatus, error)

SetBimi sets the domain's BIMI logo by URL and returns the updated status.

func (s *DomainsService) UploadBimiLogo(ctx context.Context, domain, filename string, r io.Reader) (*BimiStatus, error)

UploadBimiLogo uploads an SVG logo (<=1 MiB) for the domain's BIMI record and returns the updated status. filename is the multipart form filename; when empty it defaults to "logo.svg". r supplies the SVG bytes.

func (*DomainsService) UploadBimiLogoBytes

func (s *DomainsService) UploadBimiLogoBytes(ctx context.Context, domain, filename string, data []byte) (*BimiStatus, error)

UploadBimiLogoBytes is a convenience wrapper over UploadBimiLogo for an in-memory SVG byte slice.

func (*DomainsService) Verify

func (s *DomainsService) Verify(ctx context.Context, domain string) (*DomainVerification, error)

Verify re-checks a domain's DNS records and returns the verification result.

type EmailSendParams

type EmailSendParams struct {
	From           string
	To             string
	Subject        string
	HTML           string
	Text           string
	MessageType    string
	TemplateID     string
	TemplateVars   map[string]any
	Metadata       map[string]any
	IdempotencyKey string
}

EmailSendParams are the ergonomic inputs for Emails.Send.

type EmailsService

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

EmailsService sends email via the unified send endpoint.

func (*EmailsService) Send

Send dispatches a single email. When IdempotencyKey is empty the SDK auto-generates a UUID.

type Error

type Error struct {
	// Status is the HTTP status code (0 when the failure was a transport error).
	Status int
	// Code is the machine-readable error code from the API envelope.
	Code string
	// Message is the human-readable error message.
	Message string
	// RetryAfter is the parsed Retry-After header on a 429/503, if present.
	RetryAfter time.Duration
	// RequestID echoes any request identifier the server returned.
	RequestID string
	// contains filtered or unexported fields
}

Error is the typed error returned for every non-2xx API response. It exposes the HTTP status, the machine-readable error code, and the human-readable message from the API's error envelope: {"error":{"code","message","status"}}.

Use the Is* helpers or errors.As to branch on the failure class:

var apiErr *sendara.Error
if errors.As(err, &apiErr) && apiErr.IsRateLimited() {
    time.Sleep(apiErr.RetryAfter)
}

func (*Error) Error

func (e *Error) Error() string

func (*Error) IsAuthentication

func (e *Error) IsAuthentication() bool

IsAuthentication reports whether the request failed authentication (401).

func (*Error) IsConflict

func (e *Error) IsConflict() bool

IsConflict reports whether the request conflicted with server state (409), e.g. an idempotency-key payload mismatch or a duplicate resource.

func (*Error) IsNotFound

func (e *Error) IsNotFound() bool

IsNotFound reports whether the resource was not found (404).

func (*Error) IsPermission

func (e *Error) IsPermission() bool

IsPermission reports whether the request was forbidden (403).

func (*Error) IsRateLimited

func (e *Error) IsRateLimited() bool

IsRateLimited reports whether the request was rate limited (429).

func (*Error) IsServer

func (e *Error) IsServer() bool

IsServer reports whether the API returned a 5xx server error.

func (*Error) IsTransport

func (e *Error) IsTransport() bool

IsTransport reports whether the failure was a network/transport error rather than an HTTP response from the API.

func (*Error) IsValidation

func (e *Error) IsValidation() bool

IsValidation reports whether the request was rejected as invalid (400/422).

func (*Error) Unwrap

func (e *Error) Unwrap() error

type HTTPDoer

type HTTPDoer interface {
	Do(req *http.Request) (*http.Response, error)
}

HTTPDoer is the minimal HTTP client surface the SDK depends on, satisfied by *http.Client. Override it via WithHTTPClient to inject a custom transport.

type ImportContactsParams

type ImportContactsParams struct {
	S3Key  string `json:"s3_key"`
	Format string `json:"format"`
}

ImportContactsParams imports contacts from a staged S3 object.

type ImportResult

type ImportResult struct {
	SuccessCount int              `json:"success_count"`
	ErrorCount   int              `json:"error_count"`
	Errors       []ImportRowError `json:"errors"`
}

ImportResult is the outcome of a contact import.

type ImportRowError

type ImportRowError struct {
	Row     int    `json:"row"`
	Message string `json:"message"`
}

ImportRowError describes a failed row during import.

type ListBroadcastsParams

type ListBroadcastsParams struct {
	Limit  int
	Offset int
}

ListBroadcastsParams paginates the broadcast listing.

type ListMessagesParams

type ListMessagesParams struct {
	Channel string
	Status  string
	From    time.Time
	To      time.Time
	Limit   int
	Cursor  string
}

ListMessagesParams filters and paginates GET /v1/messages.

type ListsService

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

ListsService manages contact lists, nested under ContactsService.Lists.

func (*ListsService) AddMember

func (s *ListsService) AddMember(ctx context.Context, listID, contactID string) (*ContactListMember, error)

AddMember adds a contact to a static list.

func (*ListsService) Create

func (s *ListsService) Create(ctx context.Context, params CreateListParams) (*ContactList, error)

Create creates a contact list.

func (*ListsService) Delete

func (s *ListsService) Delete(ctx context.Context, id string) error

Delete removes a contact list.

func (*ListsService) Get

func (s *ListsService) Get(ctx context.Context, id string) (*ContactList, error)

Get returns a single contact list.

func (*ListsService) List

func (s *ListsService) List(ctx context.Context) ([]ContactList, error)

List returns the account's contact lists.

func (*ListsService) Members

func (s *ListsService) Members(ctx context.Context, listID string) ([]Contact, error)

Members returns the contacts that are members of a list.

func (*ListsService) RemoveMember

func (s *ListsService) RemoveMember(ctx context.Context, listID, contactID string) error

RemoveMember removes a contact from a static list.

func (*ListsService) Update

func (s *ListsService) Update(ctx context.Context, id string, params UpdateListParams) (*ContactList, error)

Update patches a contact list.

type Message

type Message struct {
	ID                string          `json:"id"`
	AccountID         string          `json:"account_id"`
	Channel           string          `json:"channel"`
	Status            string          `json:"status"`
	Destination       json.RawMessage `json:"destination"`
	Payload           json.RawMessage `json:"payload"`
	MessageType       string          `json:"message_type"`
	TemplateID        *string         `json:"template_id,omitempty"`
	IdempotencyKey    string          `json:"idempotency_key"`
	ProviderMessageID *string         `json:"provider_message_id,omitempty"`
	Metadata          json.RawMessage `json:"metadata,omitempty"`
	CreatedAt         time.Time       `json:"created_at"`
	UpdatedAt         time.Time       `json:"updated_at"`
	Events            []MessageEvent  `json:"events,omitempty"`
}

Message is the full message detail returned by GET /v1/messages/{id}.

type MessageEvent

type MessageEvent struct {
	ID              string          `json:"id"`
	MessageID       string          `json:"message_id"`
	AccountID       string          `json:"account_id"`
	Source          string          `json:"source"`
	ProviderEventID string          `json:"provider_event_id"`
	Type            string          `json:"type"`
	Payload         json.RawMessage `json:"payload"`
	OccurredAt      time.Time       `json:"occurred_at"`
	CreatedAt       time.Time       `json:"created_at"`
}

MessageEvent is a single event in a message's timeline.

type MessageIterator

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

MessageIterator walks every message matching a filter, transparently following the opaque cursor across pages.

func (*MessageIterator) All

All eagerly collects every message matching the iterator's filter. Use with care on large accounts; prefer streaming with Next.

func (*MessageIterator) Err

func (it *MessageIterator) Err() error

Err returns the first error encountered while iterating, if any.

func (*MessageIterator) Message

func (it *MessageIterator) Message() MessageSummary

Message returns the item the iterator is currently positioned on.

func (*MessageIterator) Next

func (it *MessageIterator) Next(ctx context.Context) bool

Next advances the iterator. It returns false when the listing is exhausted or an error occurred; check Err after the loop. Use Message to read the current item.

it := client.Messages.Iterator(sendara.ListMessagesParams{Channel: "email"})
for it.Next(ctx) {
    fmt.Println(it.Message().ID)
}
if err := it.Err(); err != nil { ... }

type MessagePage

type MessagePage struct {
	Messages   []MessageSummary `json:"messages"`
	NextCursor *string          `json:"next_cursor"`
}

MessagePage is one page of a message listing.

type MessageSummary

type MessageSummary struct {
	ID          string    `json:"id"`
	Channel     string    `json:"channel"`
	Status      string    `json:"status"`
	MessageType string    `json:"message_type"`
	CreatedAt   time.Time `json:"created_at"`
}

MessageSummary is the list representation for GET /v1/messages.

type MessagesService

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

MessagesService queries the message timeline (GET /v1/messages).

func (*MessagesService) Get

func (s *MessagesService) Get(ctx context.Context, id string) (*Message, error)

Get returns a single message with its full event timeline.

func (*MessagesService) Iterator

func (s *MessagesService) Iterator(params ListMessagesParams) *MessageIterator

Iterator returns an auto-paginating iterator over messages matching params.

func (*MessagesService) List

List returns a single page of messages plus the next cursor (nil when the listing is exhausted). For automatic pagination use Iterator.

type Option

type Option func(*Client)

Option configures a Client.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL overrides the API base URL (default https://api.sendara.dev).

func WithHTTPClient

func WithHTTPClient(hc HTTPDoer) Option

WithHTTPClient supplies a custom HTTP client. Passing nil is a no-op.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries bounds the number of automatic retries on rate-limit, server, and transport errors. Zero disables retries.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the per-request timeout on the default HTTP client. It has no effect when a custom HTTP client is supplied via WithHTTPClient.

func WithUserAgent

func WithUserAgent(ua string) Option

WithUserAgent appends a token to the SDK's User-Agent header.

type Recipient

type Recipient struct {
	Email string         `json:"email"`
	Data  map[string]any `json:"data,omitempty"`
}

Recipient is an inline broadcast recipient with optional template variables.

type RecordResult

type RecordResult struct {
	Type   string `json:"type"`
	Status string `json:"status"`
}

RecordResult is one record's verification outcome.

type SMSSendParams

type SMSSendParams struct {
	To             string
	Body           string
	SenderID       string
	MessageType    string
	IdempotencyKey string
}

SMSSendParams are the ergonomic inputs for SMS.Send.

type SMSService

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

SMSService sends SMS via the unified send endpoint.

func (*SMSService) Send

func (s *SMSService) Send(ctx context.Context, params SMSSendParams) (*SendResponse, error)

Send dispatches a single SMS. When IdempotencyKey is empty the SDK auto-generates a UUID.

type SegmentRules

type SegmentRules struct {
	Match      string           `json:"match,omitempty"`
	Conditions []map[string]any `json:"conditions,omitempty"`
}

SegmentRules is the opaque rule set for a dynamic list.

type SendRequest

type SendRequest struct {
	Channel        string         `json:"channel"`
	IdempotencyKey string         `json:"idempotency_key,omitempty"`
	MessageType    string         `json:"message_type,omitempty"`
	Destination    map[string]any `json:"destination"`
	Payload        map[string]any `json:"payload"`
	TemplateID     string         `json:"template_id,omitempty"`
	TemplateVars   map[string]any `json:"template_vars,omitempty"`
	Metadata       map[string]any `json:"metadata,omitempty"`
	StorePayload   *bool          `json:"store_payload,omitempty"`
	TestSend       bool           `json:"test_send,omitempty"`
}

SendRequest is the raw unified send payload accepted by POST /v1/send. Most callers use the higher-level Emails.Send / SMS.Send helpers instead.

type SendResponse

type SendResponse struct {
	ID             string    `json:"id"`
	Status         string    `json:"status"`
	Channel        string    `json:"channel"`
	IdempotencyKey string    `json:"idempotency_key"`
	CreatedAt      time.Time `json:"created_at"`
}

SendResponse is returned after a successful send.

type SendService

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

SendService is the low-level unified send escape hatch (POST /v1/send and /v1/send/batch). Prefer the Emails and SMS services for typical use.

func (*SendService) Batch

func (s *SendService) Batch(ctx context.Context, reqs []SendRequest) ([]BatchItemResult, error)

Batch dispatches multiple sends in one call. Each item is processed independently and results are returned in request order. A missing IdempotencyKey on any item is auto-generated.

func (*SendService) Send

func (s *SendService) Send(ctx context.Context, req SendRequest) (*SendResponse, error)

Send dispatches a raw unified send request. When IdempotencyKey is empty the SDK auto-generates a UUID, making the call safe to retry.

type SpendCap

type SpendCap struct {
	ID              string  `json:"id"`
	AccountID       string  `json:"account_id"`
	KeyID           *string `json:"key_id,omitempty"`
	SoftLimitMicros *int64  `json:"soft_limit_micros"`
	HardLimitMicros *int64  `json:"hard_limit_micros"`
}

SpendCap is the persisted spend cap.

type SpendCapParams

type SpendCapParams struct {
	KeyID           string `json:"key_id,omitempty"`
	SoftLimitMicros *int64 `json:"soft_limit_micros"`
	HardLimitMicros *int64 `json:"hard_limit_micros"`
}

SpendCapParams sets a soft/hard monthly spend cap, optionally scoped to a key.

type SuppressionEntry

type SuppressionEntry struct {
	Channel   string    `json:"channel"`
	Recipient string    `json:"recipient"`
	State     string    `json:"state"`
	Reason    string    `json:"reason,omitempty"`
	UpdatedAt time.Time `json:"updated_at"`
}

SuppressionEntry is a suppressed recipient on a channel.

type SuppressionsService

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

SuppressionsService manages the per-channel suppression list (/v1/suppressions).

func (*SuppressionsService) Create

Create adds a recipient to the suppression list, blocking future sends to that address on the channel.

func (*SuppressionsService) Delete

func (s *SuppressionsService) Delete(ctx context.Context, channel, recipient string) error

Delete removes a recipient from the suppression list (un-suppresses).

func (*SuppressionsService) List

func (s *SuppressionsService) List(ctx context.Context, channel string) ([]SuppressionEntry, error)

List returns suppressed recipients, optionally filtered by channel ("" for all channels).

type Template

type Template struct {
	ID        string             `json:"id"`
	AccountID string             `json:"account_id"`
	Name      string             `json:"name"`
	Channel   string             `json:"channel"`
	Subject   *string            `json:"subject"`
	BodyText  *string            `json:"body_text"`
	BodyHTML  *string            `json:"body_html"`
	BodyJSON  *json.RawMessage   `json:"body_json,omitempty"`
	Variables []TemplateVariable `json:"variables"`
	Version   int                `json:"version"`
	IsActive  bool               `json:"is_active"`
	CreatedAt time.Time          `json:"created_at"`
	UpdatedAt time.Time          `json:"updated_at"`
}

Template is a stored message template.

type TemplateVariable

type TemplateVariable struct {
	Name     string `json:"name"`
	Required bool   `json:"required,omitempty"`
}

TemplateVariable describes a template input variable.

type TemplatesService

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

TemplatesService manages message templates (/v1/templates).

func (*TemplatesService) Create

Create creates a template.

func (*TemplatesService) Delete

func (s *TemplatesService) Delete(ctx context.Context, id string) error

Delete removes a template.

func (*TemplatesService) Get

func (s *TemplatesService) Get(ctx context.Context, id string) (*Template, error)

Get returns a single template.

func (*TemplatesService) List

func (s *TemplatesService) List(ctx context.Context) ([]Template, error)

List returns the account's templates.

func (*TemplatesService) Render

func (s *TemplatesService) Render(ctx context.Context, id string, vars map[string]any) (map[string]any, error)

Render renders a template with the given variables and returns the resulting channel payload (e.g. {"subject":..., "body_html":...} for email).

func (*TemplatesService) Update

func (s *TemplatesService) Update(ctx context.Context, id string, params UpdateTemplateParams) (*Template, error)

Update patches a template.

type TestRecipient

type TestRecipient struct {
	ID         string     `json:"id"`
	Email      string     `json:"email"`
	Status     string     `json:"status"`
	VerifiedAt *time.Time `json:"verified_at,omitempty"`
	CreatedAt  time.Time  `json:"created_at"`
}

TestRecipient is a verified test recipient.

type TestRecipientsService

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

TestRecipientsService manages verified test recipients (/v1/test-recipients), used to validate sends before a domain is verified.

func (*TestRecipientsService) Create

func (s *TestRecipientsService) Create(ctx context.Context, email string) (*TestRecipient, error)

Create adds a test recipient and sends a verification email to it.

func (*TestRecipientsService) Delete

func (s *TestRecipientsService) Delete(ctx context.Context, id string) error

Delete removes a test recipient.

func (*TestRecipientsService) List

List returns the account's test recipients.

func (*TestRecipientsService) Resend

func (s *TestRecipientsService) Resend(ctx context.Context, id string) error

Resend re-sends the verification email for a test recipient.

type UpdateContactParams

type UpdateContactParams struct {
	Email        **string       `json:"email,omitempty"`
	PhoneNumber  **string       `json:"phone_number,omitempty"`
	DeviceToken  **string       `json:"device_token,omitempty"`
	FirstName    *string        `json:"first_name,omitempty"`
	LastName     *string        `json:"last_name,omitempty"`
	Attributes   map[string]any `json:"attributes,omitempty"`
	Tags         []string       `json:"tags,omitempty"`
	EmailConsent *string        `json:"email_consent,omitempty"`
	SMSConsent   *string        `json:"sms_consent,omitempty"`
	PushConsent  *string        `json:"push_consent,omitempty"`
	VoiceConsent *string        `json:"voice_consent,omitempty"`
}

UpdateContactParams patches a contact. Pointer-to-pointer fields distinguish "unset" (nil) from "set to null" (non-nil pointer to nil).

type UpdateListParams

type UpdateListParams struct {
	Name         *string       `json:"name,omitempty"`
	SegmentRules *SegmentRules `json:"segment_rules,omitempty"`
}

UpdateListParams patches a contact list.

type UpdateTemplateParams

type UpdateTemplateParams struct {
	Name      *string            `json:"name,omitempty"`
	Subject   *string            `json:"subject,omitempty"`
	BodyText  *string            `json:"body_text,omitempty"`
	BodyHTML  *string            `json:"body_html,omitempty"`
	BodyJSON  *json.RawMessage   `json:"body_json,omitempty"`
	Variables []TemplateVariable `json:"variables,omitempty"`
	IsActive  *bool              `json:"is_active,omitempty"`
}

UpdateTemplateParams patches a template.

type UpdateWebhookParams

type UpdateWebhookParams struct {
	EndpointURL *string  `json:"endpoint_url,omitempty"`
	EventTypes  []string `json:"event_types,omitempty"`
	IsActive    *bool    `json:"is_active,omitempty"`
}

UpdateWebhookParams patches a webhook subscription.

type Upload

type Upload struct {
	ID          string `json:"id"`
	URL         string `json:"url"`
	ContentType string `json:"content_type"`
	Bytes       int64  `json:"bytes"`
}

Upload is the result of POST /v1/uploads.

type UploadsService

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

UploadsService uploads images for the template builder (POST /v1/uploads).

func (*UploadsService) Upload

func (s *UploadsService) Upload(ctx context.Context, filename, contentType string, r io.Reader) (*Upload, error)

Upload uploads an image. filename is the form filename, contentType is the MIME type (e.g. "image/png"), and r supplies the bytes.

func (*UploadsService) UploadBytes

func (s *UploadsService) UploadBytes(ctx context.Context, filename, contentType string, data []byte) (*Upload, error)

UploadBytes is a convenience wrapper over Upload for an in-memory byte slice.

type Usage

type Usage struct {
	Period          string         `json:"period"`
	TotalSendCount  int64          `json:"total_send_count"`
	TotalCostMicros int64          `json:"total_cost_micros"`
	Channels        []ChannelUsage `json:"channels"`
}

Usage is the account's usage summary for a period.

type UsageService

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

UsageService reads usage and configures spend caps (/v1/usage, /v1/spend-caps).

func (*UsageService) Get

func (s *UsageService) Get(ctx context.Context, period string) (*Usage, error)

Get returns the account's usage summary. An empty period defaults to the current billing period.

func (*UsageService) SetSpendCap

func (s *UsageService) SetSpendCap(ctx context.Context, params SpendCapParams) (*SpendCap, error)

SetSpendCap creates or updates a monthly spend cap. An empty KeyID sets the account-wide cap; a KeyID scopes it to that key.

type WebhookDelivery

type WebhookDelivery struct {
	ID             string          `json:"id"`
	SubscriptionID string          `json:"subscription_id"`
	EventID        string          `json:"event_id"`
	EventType      string          `json:"event_type"`
	Payload        json.RawMessage `json:"payload"`
	Status         string          `json:"status"`
	AttemptCount   int             `json:"attempt_count"`
	NextRetryAt    *time.Time      `json:"next_retry_at,omitempty"`
	ResponseStatus *int            `json:"response_status,omitempty"`
	CreatedAt      time.Time       `json:"created_at"`
}

WebhookDelivery is a single delivery attempt record.

type WebhookEvent

type WebhookEvent struct {
	EventID    string          `json:"event_id"`
	EventType  string          `json:"event_type"`
	MessageID  string          `json:"message_id"`
	AccountID  string          `json:"account_id"`
	Payload    json.RawMessage `json:"payload"`
	OccurredAt time.Time       `json:"occurred_at"`
	CreatedAt  time.Time       `json:"created_at"`
}

WebhookEvent is the decoded body of an inbound webhook delivery.

func VerifyWebhookRequest

func VerifyWebhookRequest(secret string, header http.Header, body []byte, tolerance time.Duration) (*WebhookEvent, error)

VerifyWebhookRequest verifies an inbound *http.Request's signature headers against body and secret, returning the decoded event on success. The caller must read the raw body (e.g. with io.ReadAll) before calling, because the signature is computed over the exact bytes. Pass tolerance 0 to skip the replay-window check.

type WebhookSubscription

type WebhookSubscription struct {
	ID                    string    `json:"id"`
	AccountID             string    `json:"account_id"`
	EndpointURL           string    `json:"endpoint_url"`
	SigningSecret         string    `json:"signing_secret"`
	PreviousSigningSecret *string   `json:"previous_signing_secret,omitempty"`
	EventTypes            []string  `json:"event_types"`
	IsActive              bool      `json:"is_active"`
	CreatedAt             time.Time `json:"created_at"`
}

WebhookSubscription is a customer webhook endpoint subscription.

type WebhooksService

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

WebhooksService manages outbound webhook subscriptions (/v1/webhooks). To verify the signature of inbound webhook deliveries, use VerifyWebhook.

func (*WebhooksService) Create

Create creates a webhook subscription. The returned subscription includes the signing secret used to verify deliveries.

func (*WebhooksService) Delete

func (s *WebhooksService) Delete(ctx context.Context, id string) error

Delete removes a webhook subscription.

func (*WebhooksService) Deliveries

func (s *WebhooksService) Deliveries(ctx context.Context, id string) ([]WebhookDelivery, error)

Deliveries returns recent delivery attempts for a subscription.

func (*WebhooksService) Get

Get returns a single webhook subscription.

func (*WebhooksService) List

List returns the account's webhook subscriptions.

func (*WebhooksService) RotateSecret

func (s *WebhooksService) RotateSecret(ctx context.Context, id string) (*WebhookSubscription, error)

RotateSecret rotates the subscription's signing secret, returning the updated subscription (with the new secret and the previous one for overlap).

func (*WebhooksService) Update

Update patches a webhook subscription.

Jump to

Keyboard shortcuts

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