goomzo

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Nov 13, 2026 License: MIT Imports: 21 Imported by: 0

README

goomzo-go

The Goomzo API for Go: send SMS, WhatsApp and email messages, send saved templates, keep customers in step, and check webhooks. Go 1.24 or later, no dependencies.

go get github.com/goomzo/goomzo-go
import "github.com/goomzo/goomzo-go"

client, err := goomzo.NewClient() // reads GOOMZO_API_KEY
if err != nil {
	return err
}

// One message.
_, err = client.Messages.SendOne(ctx, goomzo.MessageInput{
	To:      "+255712345678",
	Channel: goomzo.SMS,
	Content: &goomzo.Content{Text: "Your order is ready."},
})

// A saved template. {{secret.code}} is delivered, but kept as ••••••.
_, err = client.Messages.SendOne(ctx, goomzo.MessageInput{
	To:        "+255712345678",
	Template:  goomzo.SavedTemplate("auth_1"),
	Variables: map[string]string{"code": "482913"},
})

// Every customer in a segment, a page at a time behind the scenes.
for customer, err := range client.Segments.Customers.List(ctx, "seg_4hq2v9n1c8x0t6pk") {
	if err != nil {
		return err
	}
	fmt.Println(customer.Name)
}

What it does for you

  • Typed: every request and answer is a struct, checked against the API's own description.
  • A fixed version: it pins the API version it was written for.
  • Safe retries: network errors, 429 and 5xx are tried again, twice by default, within your context's deadline. Every POST carries an Idempotency-Key, the same on each try, so nothing is done twice.
  • Lists page themselves: range over a list for every item.
  • Errors you can act on: an *APIError has the Status, the Code, the Fields that were wrong and the RequestID.

Webhooks

body, _ := io.ReadAll(r.Body)
event, err := goomzo.VerifyWebhook(body, r.Header, os.Getenv("GOOMZO_WEBHOOK_SECRET"))
if err != nil {
	http.Error(w, "bad signature", http.StatusBadRequest)
	return
}
if event.Type == "message.delivered" {
	// …
}

Issues

This repository is published from Goomzo's main repository on each release. For questions and issues, write to hello@goomzo.africa.

Documentation

Overview

Package goomzo is the Goomzo API for Go: send SMS, WhatsApp and email messages, send saved templates, keep customers in step, and check webhooks.

client, err := goomzo.NewClient() // reads GOOMZO_API_KEY
if err != nil {
	return err
}
msg, err := client.Messages.SendOne(ctx, goomzo.MessageInput{
	To:        "+255712345678",
	Template:  goomzo.SavedTemplate("auth_1"),
	Variables: map[string]string{"code": "482913"},
})

Every call takes a context, and retries network errors, 429 and 5xx with the same Idempotency-Key, so nothing is done twice. Errors from the API are *APIError; use errors.As to read its Code and Fields.

Docs: https://docs.goomzo.africa/docs/developers/go-sdk

Index

Constants

View Source
const APIVersion = "2026-11-08"

APIVersion is the API version this release is written for. Every request is sent with it, as Goomzo-Version.

View Source
const DefaultBaseURL = "https://api.goomzo.africa"

DefaultBaseURL is where the API is.

Variables

This section is empty.

Functions

This section is empty.

Types

type APIError

type APIError struct {
	Status    int
	Code      string
	Message   string
	Fields    map[string]FieldProblem
	RequestID string
}

APIError is an error the API answered with.

Code says what went wrong, such as "invalid", "not_found" or "insufficient_sms". Fields names the fields that were wrong, such as "customers[1].phone". Include RequestID when you contact Goomzo.

func (*APIError) Error

func (e *APIError) Error() string

type Activity

type Activity struct {
	ID         string    `json:"id"`
	CustomerID string    `json:"customerId"`
	Key        string    `json:"key"`
	Value      any       `json:"value"`
	OccurredAt time.Time `json:"occurredAt"`
	CreatedAt  time.Time `json:"createdAt"`
}

Activity is one recorded customer activity.

type ActivityInput

type ActivityInput struct {
	// Key is the activity: lower case letters, numbers and _, such as "purchased".
	Key string `json:"key"`
	// Value is text, a number or a bool.
	Value any `json:"value"`
	// OccurredAt is when it happened. Left out, now.
	OccurredAt *time.Time `json:"occurredAt,omitempty"`
}

ActivityInput is one activity to record.

type ActivityRecordParams

type ActivityRecordParams struct {
	Phone      string `json:"phone,omitempty"`
	Email      string `json:"email,omitempty"`
	CustomerID string `json:"customerId,omitempty"`
	// Activities are up to 100, recorded all or none.
	Activities []ActivityInput `json:"activities"`
}

ActivityRecordParams is customer activity to record. Give exactly one of Phone, Email or CustomerID.

type ActivityService

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

ActivityService records customer activity, by phone, email or id.

func (*ActivityService) Record

Record records up to 100 activities for a customer, all or none. No customer with the phone, email or id is an *APIError with Code "not_found", and nobody is added.

_, err := client.Activity.Record(ctx, goomzo.ActivityRecordParams{
	Phone:      "+255712345678",
	Activities: []goomzo.ActivityInput{{Key: "purchased", Value: "Shoes"}},
})

type AttachmentInput added in v0.2.0

type AttachmentInput struct {
	ID string `json:"id"`
	// Filename is the name it goes by. Left out, the uploaded file's.
	Filename string `json:"filename,omitempty"`
}

AttachmentInput is a file uploaded to POST /v1/messages/files, sent with a message: on WhatsApp a photo, video, audio or document, by email an attachment.

type AudienceInput

type AudienceInput struct {
	Segments []SegmentRef `json:"segments,omitempty"`
	To       []string     `json:"to,omitempty"`
	Everyone bool         `json:"everyone,omitempty"`
}

AudienceInput is who a broadcast goes to: Segments and To together, or Everyone.

type Broadcast

type Broadcast struct {
	ID         string            `json:"id"`
	Name       string            `json:"name"`
	Channel    Channel           `json:"channel"`
	From       *string           `json:"from"`
	Content    Content           `json:"content"`
	Audience   BroadcastAudience `json:"audience"`
	Status     BroadcastStatus   `json:"status"`
	Error      *string           `json:"error"`
	Recipients int               `json:"recipients"`
	Sent       int               `json:"sent"`
	Delivered  int               `json:"delivered"`
	Failed     int               `json:"failed"`
	Read       int               `json:"read"`
	SendAt     time.Time         `json:"sendAt"`
	CreatedAt  time.Time         `json:"createdAt"`
	Test       bool              `json:"test"`
	Template   *TemplateRef      `json:"template"`
}

Broadcast is one message sent to many people.

type BroadcastAudience

type BroadcastAudience struct {
	// Name is in words, such as "VIP and 2 numbers".
	Name     string             `json:"name"`
	Everyone bool               `json:"everyone"`
	Segments []BroadcastSegment `json:"segments"`
	ToCount  int                `json:"toCount"`
}

BroadcastAudience is who a broadcast went to.

type BroadcastInput

type BroadcastInput struct {
	Name     string        `json:"name"`
	Channel  Channel       `json:"channel"`
	Audience AudienceInput `json:"audience"`
	From     string        `json:"from,omitempty"`
	// Content is required for SMS. It can use {{name}} and {{attributes.x}}, filled in for each person.
	Content *Content `json:"content,omitempty"`
	// Template is, on WhatsApp, an approved template.
	Template *WhatsAppTemplateInput `json:"template,omitempty"`
	SendAt   *time.Time             `json:"sendAt,omitempty"`
}

BroadcastInput is a broadcast to send.

type BroadcastListParams

type BroadcastListParams struct {
	Q       string
	Channel Channel
	Status  BroadcastStatus
	// Limit is the size of each page, up to 200.
	Limit  int
	Cursor string
}

BroadcastListParams filters a list of broadcasts.

type BroadcastSegment

type BroadcastSegment struct {
	ID   string `json:"id"`
	Name string `json:"name"`
}

BroadcastSegment is a segment a broadcast went to.

type BroadcastStatus

type BroadcastStatus string

BroadcastStatus is where a broadcast is.

const (
	BroadcastScheduled BroadcastStatus = "scheduled"
	BroadcastSending   BroadcastStatus = "sending"
	BroadcastSent      BroadcastStatus = "sent"
	BroadcastFailed    BroadcastStatus = "failed"
	BroadcastCanceled  BroadcastStatus = "canceled"
)

type BroadcastsService

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

BroadcastsService sends one message to segments, a list of numbers, or every customer, filled in for each person.

Docs: https://docs.goomzo.africa/docs/developers/messages/broadcasts

func (*BroadcastsService) Cancel

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

Cancel cancels a scheduled broadcast before it goes.

func (*BroadcastsService) Create

Create sends a broadcast, now or at SendAt. Audience is required: an empty one is an error, never a send to everyone.

b, err := client.Broadcasts.Create(ctx, goomzo.BroadcastInput{
	Name:     "Black Friday",
	Channel:  goomzo.SMS,
	Content:  &goomzo.Content{Text: "Hi {{name|there}}, 20% off today."},
	Audience: goomzo.AudienceInput{Segments: []goomzo.SegmentRef{{Name: "VIP"}}},
})

func (*BroadcastsService) Get

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

Get gets a broadcast, with how its messages are doing.

func (*BroadcastsService) List

List iterates every broadcast, newest first, asking for each page as you go.

func (*BroadcastsService) ListPage

ListPage gets one page of broadcasts, newest first.

type Channel

type Channel string

Channel is how a message goes.

const (
	SMS      Channel = "sms"
	WhatsApp Channel = "whatsapp"
)

type Client

type Client struct {

	// Messages sends SMS, WhatsApp and email messages, each to one person.
	Messages *MessagesService
	// Broadcasts sends one message to many people.
	Broadcasts *BroadcastsService
	// Templates are saved templates, sent by name.
	Templates *TemplatesService
	// WhatsApp is the business's WhatsApp account and its templates.
	WhatsApp *WhatsAppService
	// Customers adds and updates customers.
	Customers *CustomersService
	// Activity records customer activity, by phone, email or id.
	Activity *ActivityService
	// Segments are groups of customers.
	Segments *SegmentsService
	// WebhookEndpoints are URLs that hear webhook events.
	WebhookEndpoints *WebhookEndpointsService
	// contains filtered or unexported fields
}

Client is the Goomzo API client. Make one and share it: it's safe to use from many goroutines.

func NewClient

func NewClient(opts ...Option) (*Client, error)

NewClient makes a client. Without WithAPIKey, it reads GOOMZO_API_KEY, and fails when neither is set.

type ConnectionError

type ConnectionError struct {
	TimedOut bool
	Err      error
}

ConnectionError means no answer came: the network failed, or the request took longer than the timeout.

func (*ConnectionError) Error

func (e *ConnectionError) Error() string

func (*ConnectionError) Unwrap

func (e *ConnectionError) Unwrap() error

type Content

type Content struct {
	Text    string `json:"text"`
	Subject string `json:"subject,omitempty"`
	// Design is a designed email (the API's EmailDesign), for email templates and broadcasts only; its plain text becomes Text.
	Design json.RawMessage `json:"design,omitempty"`
}

Content is what a message says.

type Customer

type Customer struct {
	ID         string            `json:"id"`
	Name       string            `json:"name"`
	Phone      *string           `json:"phone"`
	Email      *string           `json:"email"`
	Attributes map[string]any    `json:"attributes"`
	Segments   []CustomerSegment `json:"segments"`
	CreatedAt  time.Time         `json:"createdAt"`
	UpdatedAt  time.Time         `json:"updatedAt"`
}

Customer is someone the business knows, by phone, email or both.

type CustomerActivityService

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

CustomerActivityService records and reads a customer's activity, by their id.

func (*CustomerActivityService) List

func (s *CustomerActivityService) List(ctx context.Context, customerID string) iter.Seq2[Activity, error]

List iterates a customer's activity, newest first.

func (*CustomerActivityService) ListPage

func (s *CustomerActivityService) ListPage(ctx context.Context, customerID string, limit int, cursor string) (*Page[Activity], error)

ListPage gets one page of a customer's activity, newest first.

func (*CustomerActivityService) Record

func (s *CustomerActivityService) Record(ctx context.Context, customerID string, activities []ActivityInput, opts ...RequestOption) (*RecordedActivity, error)

Record records a customer's activity, by their id. To find them by phone or email, use Client.Activity.

type CustomerCreateParams

type CustomerCreateParams struct {
	// Customers are up to 100, saved all or none.
	Customers []NewCustomer `json:"customers"`
	// IfExists is "error", the default, or "update": a customer whose phone or email someone already has is
	// updated instead.
	IfExists string `json:"ifExists,omitempty"`
	// IfSegmentMissing is "error", the default, or "create": segments named by Name are added.
	IfSegmentMissing string `json:"ifSegmentMissing,omitempty"`
}

CustomerCreateParams are customers to add, with what to do when they already exist.

type CustomerInput

type CustomerInput struct {
	Name string `json:"name,omitempty"`
	// Phone has its country code, such as "+255712345678".
	Phone string `json:"phone,omitempty"`
	Email string `json:"email,omitempty"`
	// Attributes are used as {{attributes.x}}. When sent, they replace the customer's.
	Attributes map[string]any `json:"attributes,omitempty"`
}

CustomerInput is a customer's details, or what to change in them.

type CustomerListParams

type CustomerListParams struct {
	Q            string
	Segment      string
	NotInSegment string
	// Sort is "-createdAt", the default, "createdAt", "name" or "-name".
	Sort string
	// Limit is the size of each page, up to 200.
	Limit  int
	Cursor string
}

CustomerListParams filters a list of customers.

type CustomerSegment

type CustomerSegment struct {
	ID    string `json:"id"`
	Name  string `json:"name"`
	Color string `json:"color"`
}

CustomerSegment is a segment a customer is in.

type CustomerSegmentsService

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

CustomerSegmentsService manages the segments a customer is in.

func (*CustomerSegmentsService) List

func (s *CustomerSegmentsService) List(ctx context.Context, customerID string) ([]Segment, error)

List lists the segments a customer is in: picked by hand, and ones whose rules they match.

func (*CustomerSegmentsService) Set

func (s *CustomerSegmentsService) Set(ctx context.Context, customerID string, names []string) ([]Segment, error)

Set puts a customer in exactly these segments, by name, adding ones that don't exist yet.

type CustomersService

type CustomersService struct {

	// Segments are the segments a customer is in.
	Segments *CustomerSegmentsService
	// Activity is a customer's activity.
	Activity *CustomerActivityService
	// contains filtered or unexported fields
}

CustomersService manages the people the business knows, with their segments and activity.

Docs: https://docs.goomzo.africa/docs/developers/go-sdk/customers

func (*CustomersService) Create

Create adds up to 100 customers, with their segments and activity, all or none, and returns what became of each.

saved, err := client.Customers.Create(ctx, goomzo.CustomerCreateParams{
	IfExists:  "update",
	Customers: []goomzo.NewCustomer{{Name: "Amina Juma", Phone: "+255712345678", Segments: []goomzo.SegmentRef{{Name: "Shoppers"}}}},
})

func (*CustomersService) Delete

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

Delete deletes a customer.

func (*CustomersService) Get

func (s *CustomersService) Get(ctx context.Context, id string) (*Customer, error)

Get gets a customer by their id.

func (*CustomersService) List

List iterates every customer, asking for each page as you go.

func (*CustomersService) ListPage

func (s *CustomersService) ListPage(ctx context.Context, params CustomerListParams) (*Page[Customer], error)

ListPage gets one page of customers, newest first.

func (*CustomersService) Update

func (s *CustomersService) Update(ctx context.Context, id string, changes CustomerInput) (*Customer, error)

Update changes a customer. What's left out stays as it was; Attributes, when sent, replace theirs.

type FieldProblem

type FieldProblem struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

FieldProblem is a problem with one field of a request.

type Media

type Media struct {
	Content     []byte
	ContentType string
}

Media is a file a customer sent on WhatsApp.

type Message

type Message struct {
	ID          string    `json:"id"`
	BroadcastID *string   `json:"broadcastId"`
	CustomerID  *string   `json:"customerId"`
	To          MessageTo `json:"to"`
	Channel     Channel   `json:"channel"`
	From        *string   `json:"from"`
	// Content is what it says. A private message's secrets show as ••••••.
	Content     Content       `json:"content"`
	Status      MessageStatus `json:"status"`
	Error       *string       `json:"error"`
	Cost        int           `json:"cost"`
	SendAt      time.Time     `json:"sendAt"`
	SentAt      *time.Time    `json:"sentAt"`
	DeliveredAt *time.Time    `json:"deliveredAt"`
	ReadAt      *time.Time    `json:"readAt"`
	FailedAt    *time.Time    `json:"failedAt"`
	CreatedAt   time.Time     `json:"createdAt"`
	Test        bool          `json:"test"`
	// Private means it was sent with secrets, which Goomzo never shows.
	Private  bool         `json:"private"`
	Template *TemplateRef `json:"template"`
	// Attachment is the file it carries, if any.
	Attachment *MessageFile `json:"attachment"`
}

Message is a message to one person.

type MessageFile added in v0.2.0

type MessageFile struct {
	ID       string `json:"id"`
	Filename string `json:"filename"`
	MimeType string `json:"mimeType"`
	// Size is in bytes.
	Size int `json:"size"`
}

MessageFile is the file a sent message carries, served at GET /v1/messages/{id}/file.

type MessageInput

type MessageInput struct {
	// To is a phone number with its country code, such as "+255712345678".
	To         string  `json:"to,omitempty"`
	CustomerID string  `json:"customerId,omitempty"`
	Channel    Channel `json:"channel,omitempty"`
	// From is an SMS sender ID from Messages.Senders. Left out, yours or Goomzo's shared one.
	From    string   `json:"from,omitempty"`
	Content *Content `json:"content,omitempty"`
	// Template is SavedTemplate("auth_1"), or WhatsAppTemplateByID(id, values) to send one directly.
	Template *TemplateChoice `json:"template,omitempty"`
	// Variables are the template's {{data.x}} and {{secret.x}} values. Secrets are delivered, but kept as ••••••.
	Variables map[string]string `json:"variables,omitempty"`
	// SendAt is when to send it. Left out, now.
	SendAt *time.Time `json:"sendAt,omitempty"`
	// ReplyTo is, on WhatsApp, a message the customer sent, which this reply quotes.
	ReplyTo string `json:"replyTo,omitempty"`
	// Attachment is a file to send with it; Content.Text is then its caption on WhatsApp.
	Attachment *AttachmentInput `json:"attachment,omitempty"`
}

MessageInput is a message to send: To or CustomerID, and Content or a Template.

type MessageListParams

type MessageListParams struct {
	Q        string
	Customer string
	Channel  Channel
	Status   MessageStatus
	// Limit is the size of each page, up to 200.
	Limit  int
	Cursor string
}

MessageListParams filters a list of messages.

type MessageStatus

type MessageStatus string

MessageStatus is where a message is. Waiting is a WhatsApp reply held until the customer opens the chat.

const (
	MessageWaiting   MessageStatus = "waiting"
	MessageScheduled MessageStatus = "scheduled"
	MessageSending   MessageStatus = "sending"
	MessageSent      MessageStatus = "sent"
	MessageDelivered MessageStatus = "delivered"
	MessageRead      MessageStatus = "read"
	MessageFailed    MessageStatus = "failed"
	MessageCanceled  MessageStatus = "canceled"
)

type MessageTemplate

type MessageTemplate struct {
	ID       string           `json:"id"`
	Name     string           `json:"name"`
	Channel  Channel          `json:"channel"`
	Category TemplateCategory `json:"category"`
	// Content is an SMS template's words. Nil for WhatsApp.
	Content  *Content               `json:"content"`
	WhatsApp *SavedWhatsAppTemplate `json:"whatsapp"`
	// Variables are what your system passes when it sends it.
	Variables []TemplateVariable `json:"variables"`
	// Problem is why it can't be sent now, such as a WhatsApp template still in review.
	Problem           *string   `json:"problem"`
	LibraryTemplateID *string   `json:"libraryTemplateId"`
	CreatedAt         time.Time `json:"createdAt"`
	UpdatedAt         time.Time `json:"updatedAt"`
}

MessageTemplate is a saved template, sent by name.

type MessageTo

type MessageTo struct {
	Name  string `json:"name"`
	Phone string `json:"phone,omitempty"`
	Email string `json:"email,omitempty"`
}

MessageTo is who a message went to.

type MessagesService

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

MessagesService sends messages, each to one person, by SMS, WhatsApp or email.

Docs: https://docs.goomzo.africa/docs/developers/go-sdk/messages

func (*MessagesService) Cancel

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

Cancel cancels a scheduled message before it goes. A message already sending can't be canceled.

func (*MessagesService) Get

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

Get gets a message by its id.

func (*MessagesService) List

List iterates every message, newest first, asking for each page as you go.

for msg, err := range client.Messages.List(ctx, goomzo.MessageListParams{Status: goomzo.MessageFailed}) {
	if err != nil {
		return err
	}
	fmt.Println(msg.ID, *msg.Error)
}

func (*MessagesService) ListPage

func (s *MessagesService) ListPage(ctx context.Context, params MessageListParams) (*Page[Message], error)

ListPage gets one page of messages, newest first. Send the page's NextCursor as Cursor for the next.

func (*MessagesService) Media

func (s *MessagesService) Media(ctx context.Context, receivedID string) (*Media, error)

Media gets a file a customer sent on WhatsApp, such as a photo or voice note, by the received message's id. It's an *APIError with Code "media_not_ready" while Goomzo is still fetching it.

func (*MessagesService) Send

func (s *MessagesService) Send(ctx context.Context, msgs []MessageInput, opts ...RequestOption) ([]Message, error)

Send sends up to 100 messages, each to its own person, and returns them in order. They're queued all or none: if one isn't valid, none is sent, and the error names it, such as "messages[1].to".

func (*MessagesService) SendOne

func (s *MessagesService) SendOne(ctx context.Context, msg MessageInput, opts ...RequestOption) (*Message, error)

SendOne sends one message, and returns it.

msg, err := client.Messages.SendOne(ctx, goomzo.MessageInput{
	To:      "+255712345678",
	Channel: goomzo.SMS,
	Content: &goomzo.Content{Text: "Your order is ready."},
})

func (*MessagesService) Senders

func (s *MessagesService) Senders(ctx context.Context) (*Senders, error)

Senders lists the sender IDs the business can send from, to use as From.

type NewCustomer

type NewCustomer struct {
	Name       string          `json:"name,omitempty"`
	Phone      string          `json:"phone,omitempty"`
	Email      string          `json:"email,omitempty"`
	Attributes map[string]any  `json:"attributes,omitempty"`
	Segments   []SegmentRef    `json:"segments,omitempty"`
	Activities []ActivityInput `json:"activities,omitempty"`
}

NewCustomer is a customer to add, with their segments and activity.

type Option

type Option func(*Client)

Option changes how a Client connects.

func WithAPIKey

func WithAPIKey(key string) Option

WithAPIKey sets your API key's secret, gzk_…. Left out, GOOMZO_API_KEY is used. Keep it on your server.

func WithBaseURL

func WithBaseURL(u string) Option

WithBaseURL sets where the API is. Left out, DefaultBaseURL.

func WithHTTPClient

func WithHTTPClient(h *http.Client) Option

WithHTTPClient sets your own *http.Client, such as for a proxy or tests.

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets how many times a request is tried again after a network error, a 429 or a 5xx. Left out, 2.

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets how long to wait for each try. Left out, 60 seconds.

type Page

type Page[T any] struct {
	Items []T `json:"items"`
	// NextCursor is the cursor of the next page, or "" on the last. Send it as the params' Cursor.
	NextCursor string `json:"nextCursor"`
	// Total is how many match, on every page. Only customers, messages and broadcasts have it.
	Total *int `json:"total"`
}

Page is one page of a list.

type RecordedActivity

type RecordedActivity struct {
	CustomerID string     `json:"customerId"`
	Items      []Activity `json:"items"`
}

RecordedActivity is the customer activity just recorded.

type RequestOption

type RequestOption func(*requestConfig)

RequestOption changes one request.

func WithIdempotencyKey

func WithIdempotencyKey(key string) RequestOption

WithIdempotencyKey sends your own Idempotency-Key with a POST, such as "order-1042-ready", so a retry from your side is done once. Left out, one is made for each call.

type SavedCustomer

type SavedCustomer struct {
	// Result is "created", "updated" or "unchanged".
	Result     string       `json:"result"`
	Customer   Customer     `json:"customer"`
	Segments   []SegmentRef `json:"segments"`
	Activities []Activity   `json:"activities"`
}

SavedCustomer is what became of a customer sent to Customers.Create.

type SavedWhatsAppTemplate

type SavedWhatsAppTemplate struct {
	Name       string            `json:"name"`
	Language   string            `json:"language"`
	TemplateID *string           `json:"templateId,omitempty"`
	Values     map[string]string `json:"values"`
}

SavedWhatsAppTemplate is the WhatsApp template a saved template sends.

type Segment

type Segment struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Color       string `json:"color"`
	Description string `json:"description"`
	// Kind is "manual" or "rules".
	Kind          string        `json:"kind"`
	Rules         *SegmentRules `json:"rules"`
	CustomerCount *int          `json:"customerCount"`
	CreatedAt     time.Time     `json:"createdAt"`
	UpdatedAt     time.Time     `json:"updatedAt"`
}

Segment is a group of customers, picked by hand or filled by rules.

type SegmentCondition

type SegmentCondition struct {
	Type       string `json:"type"`
	Key        string `json:"key,omitempty"`
	Op         string `json:"op,omitempty"`
	Value      any    `json:"value,omitempty"`
	Count      int    `json:"count,omitempty"`
	WithinDays int    `json:"withinDays,omitempty"`
	SegmentID  string `json:"segmentId,omitempty"`
}

SegmentCondition is one thing a customer must match, such as {Type: "activity", Key: "purchased", Op: "atLeast"}.

type SegmentCustomersService

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

SegmentCustomersService manages the customers in a segment.

func (*SegmentCustomersService) Add

func (s *SegmentCustomersService) Add(ctx context.Context, segmentID string, customerIDs []string) (*Segment, error)

Add adds up to 1,000 customers, by id, to a segment picked by hand.

func (*SegmentCustomersService) List

List iterates every customer in a segment, asking for each page as you go.

for customer, err := range client.Segments.Customers.List(ctx, "seg_4hq2v9n1c8x0t6pk") {
	if err != nil {
		return err
	}
	fmt.Println(customer.Name)
}

func (*SegmentCustomersService) ListPage

func (s *SegmentCustomersService) ListPage(ctx context.Context, segmentID string, limit int, cursor string) (*Page[Customer], error)

ListPage gets one page of a segment's customers.

func (*SegmentCustomersService) Remove

func (s *SegmentCustomersService) Remove(ctx context.Context, segmentID string, customerIDs []string) (*Segment, error)

Remove takes up to 1,000 customers, by id, out of a segment picked by hand.

type SegmentInput

type SegmentInput struct {
	Name        string `json:"name,omitempty"`
	Color       string `json:"color,omitempty"`
	Description string `json:"description,omitempty"`
	// Rules make a new segment fill itself with the customers who match.
	Rules *SegmentRules `json:"rules,omitempty"`
}

SegmentInput is a segment to add, or what to change in one.

type SegmentRef

type SegmentRef struct {
	ID   string `json:"id,omitempty"`
	Name string `json:"name,omitempty"`
}

SegmentRef names a segment by ID or by Name.

type SegmentRules

type SegmentRules struct {
	// Match is "all" or "any".
	Match      string             `json:"match"`
	Conditions []SegmentCondition `json:"conditions"`
}

SegmentRules is who belongs to a segment that fills itself.

type SegmentsService

type SegmentsService struct {

	// Customers are the customers in a segment.
	Customers *SegmentCustomersService
	// contains filtered or unexported fields
}

SegmentsService manages groups of customers, picked by hand or filled by rules.

func (*SegmentsService) Create

func (s *SegmentsService) Create(ctx context.Context, segment SegmentInput) (*Segment, error)

Create adds a segment. With Rules, it fills itself with the customers they match.

func (*SegmentsService) Delete

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

Delete deletes a segment. Its customers stay.

func (*SegmentsService) Get

func (s *SegmentsService) Get(ctx context.Context, id string) (*Segment, error)

Get gets a segment by its id.

func (*SegmentsService) List

func (s *SegmentsService) List(ctx context.Context) ([]Segment, error)

List lists every segment, by name.

func (*SegmentsService) Update

func (s *SegmentsService) Update(ctx context.Context, id string, changes SegmentInput) (*Segment, error)

Update changes a segment. What's left out stays as it was.

type Sender

type Sender struct {
	Value  string `json:"value"`
	Shared bool   `json:"shared"`
	// Provider carries texts from an SMS sender; nil for email and WhatsApp.
	Provider *SenderProvider `json:"provider"`
}

Sender is a sender ID the business can send from.

type SenderProvider added in v0.2.0

type SenderProvider struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Own  bool   `json:"own"`
}

SenderProvider is who carries texts from an SMS sender: Goomzo SMS, or the business's own account with a provider (Own), whose texts use none of its SMS.

type Senders

type Senders struct {
	SMS      []Sender `json:"sms"`
	Email    []Sender `json:"email"`
	WhatsApp []Sender `json:"whatsapp"`
}

Senders are the sender IDs the business can send from, by channel.

type TemplateCategory

type TemplateCategory string

TemplateCategory is what a template is for: offers and news, updates someone needs, or one-time codes.

const (
	Marketing      TemplateCategory = "marketing"
	Utility        TemplateCategory = "utility"
	Authentication TemplateCategory = "authentication"
)

type TemplateChoice

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

TemplateChoice is the template a message goes as. Make one with SavedTemplate or WhatsAppTemplateByID.

func SavedTemplate

func SavedTemplate(nameOrID string) *TemplateChoice

SavedTemplate sends a saved template, by its name or id, such as "auth_1". Its channel is used.

func WhatsAppTemplateByID

func WhatsAppTemplateByID(id string, values map[string]string) *TemplateChoice

WhatsAppTemplateByID sends an approved WhatsApp template directly, with a value for each of its parameters.

func (TemplateChoice) MarshalJSON

func (t TemplateChoice) MarshalJSON() ([]byte, error)

MarshalJSON writes the template as the API takes it: a name, or {id, values}.

type TemplateInput

type TemplateInput struct {
	Name    string  `json:"name,omitempty"`
	Channel Channel `json:"channel,omitempty"`
	// Category is required for SMS. A WhatsApp template takes Meta's.
	Category TemplateCategory        `json:"category,omitempty"`
	Content  *Content                `json:"content,omitempty"`
	WhatsApp *WhatsAppTemplateChoice `json:"whatsapp,omitempty"`
}

TemplateInput is a template to save, or what to change in one.

type TemplateListParams

type TemplateListParams struct {
	Channel  Channel
	Category TemplateCategory
}

TemplateListParams filters the templates.

type TemplateRef

type TemplateRef struct {
	ID       string            `json:"id"`
	Name     string            `json:"name"`
	Language string            `json:"language"`
	Category string            `json:"category"`
	Values   map[string]string `json:"values"`
}

TemplateRef is the WhatsApp template a message went as, with what filled each parameter.

type TemplateVariable

type TemplateVariable struct {
	Key string `json:"key"`
	// Private means a {{secret.x}}: masked once sent.
	Private bool `json:"private"`
}

TemplateVariable is a value a template needs when it's sent.

type TemplatesService

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

TemplatesService manages saved templates, for SMS, WhatsApp and email. Send one by name with MessageInput.Template.

Docs: https://docs.goomzo.africa/docs/developers/go-sdk/templates

func (*TemplatesService) Create

Create saves a template. An SMS one is ready at once. A WhatsApp one with WhatsApp.Submit goes to Meta for review, and has a Problem until Meta approves it.

t, err := client.Templates.Create(ctx, goomzo.TemplateInput{
	Name:     "auth_1",
	Channel:  goomzo.SMS,
	Category: goomzo.Authentication,
	Content:  &goomzo.Content{Text: "Your code is {{secret.code}}."},
})

func (*TemplatesService) Delete

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

Delete deletes a template.

func (*TemplatesService) Get

Get gets a template by its id.

func (*TemplatesService) List

List lists the templates, by name.

func (*TemplatesService) Update

func (s *TemplatesService) Update(ctx context.Context, id string, changes TemplateInput) (*MessageTemplate, error)

Update changes a template. What's left out stays as it was.

type WebhookEndpoint

type WebhookEndpoint struct {
	ID          string   `json:"id"`
	URL         string   `json:"url"`
	Description string   `json:"description"`
	Events      []string `json:"events"`
	// Secret is the signing secret, whsec_…, to check events with VerifyWebhook.
	Secret                  string     `json:"secret"`
	Disabled                bool       `json:"disabled"`
	DisabledReason          *string    `json:"disabledReason"`
	PreviousSecretExpiresAt *time.Time `json:"previousSecretExpiresAt,omitempty"`
	CreatedAt               time.Time  `json:"createdAt"`
	UpdatedAt               time.Time  `json:"updatedAt"`
}

WebhookEndpoint is a URL that hears webhook events.

type WebhookEndpointInput

type WebhookEndpointInput struct {
	// URL is an https:// URL. Required when adding.
	URL         string `json:"url,omitempty"`
	Description string `json:"description,omitempty"`
	// Events are what it hears, such as "message.delivered". Required when adding.
	Events   []string `json:"events,omitempty"`
	Disabled *bool    `json:"disabled,omitempty"`
}

WebhookEndpointInput is an endpoint to add, or what to change in one.

type WebhookEndpointsService

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

WebhookEndpointsService manages URLs that hear events as they happen. Check each event with VerifyWebhook.

Docs: https://docs.goomzo.africa/docs/developers/go-sdk/webhooks

func (*WebhookEndpointsService) Create

Create adds an endpoint. It comes with its signing Secret: keep it, to check events with VerifyWebhook.

func (*WebhookEndpointsService) Delete

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

Delete deletes an endpoint.

func (*WebhookEndpointsService) Get

Get gets an endpoint by its id.

func (*WebhookEndpointsService) List

List lists the endpoints.

func (*WebhookEndpointsService) Update

Update changes an endpoint: its URL, its events, or whether it's on.

type WebhookEvent

type WebhookEvent struct {
	// ID is the same on every retry, so use it to skip events you've already handled.
	ID string `json:"id"`
	// Type is what happened, such as "message.delivered".
	Type           string    `json:"type"`
	Timestamp      time.Time `json:"timestamp"`
	OrganizationID string    `json:"organizationId"`
	// Data is what it's about, such as the message. Its fields depend on Type: decode it into a Message, a
	// Customer, or your own struct.
	Data json.RawMessage `json:"data"`
}

WebhookEvent is an event as Goomzo delivers it.

func VerifyWebhook

func VerifyWebhook(payload []byte, header http.Header, secret string) (*WebhookEvent, error)

VerifyWebhook checks that a webhook delivery came from Goomzo and is recent, then returns its event.

Pass the body exactly as it arrived, before decoding it: the signature is of those bytes. Pass the request's headers, and the endpoint's signing secret, whsec_…. It's a *WebhookVerificationError when the signature doesn't match, or the delivery is more than 5 minutes old.

func handle(w http.ResponseWriter, r *http.Request) {
	body, _ := io.ReadAll(r.Body)
	event, err := goomzo.VerifyWebhook(body, r.Header, os.Getenv("GOOMZO_WEBHOOK_SECRET"))
	if err != nil {
		http.Error(w, "bad signature", http.StatusBadRequest)
		return
	}
	if event.Type == "message.delivered" { … }
	w.WriteHeader(http.StatusNoContent)
}

type WebhookVerificationError

type WebhookVerificationError struct {
	Reason string
}

WebhookVerificationError means a webhook's signature or timestamp didn't check out. Don't trust its body.

func (*WebhookVerificationError) Error

func (e *WebhookVerificationError) Error() string

type WhatsAppService

type WhatsAppService struct {
	// Templates are the templates on the account, as synced from Meta.
	Templates *WhatsAppTemplatesService
}

WhatsAppService is the business's WhatsApp account.

type WhatsAppTemplate

type WhatsAppTemplate struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	Language string `json:"language"`
	Category string `json:"category"`
	// Status is Meta's, such as "APPROVED" or "PENDING".
	Status         string                      `json:"status"`
	Quality        string                      `json:"quality"`
	Problem        *string                     `json:"problem"`
	Header         *string                     `json:"header"`
	Body           string                      `json:"body"`
	Footer         *string                     `json:"footer"`
	Parameters     []WhatsAppTemplateParameter `json:"parameters"`
	CategoryChange *string                     `json:"categoryChange"`
	Preview        string                      `json:"preview"`
	SyncedAt       time.Time                   `json:"syncedAt"`
}

WhatsAppTemplate is a template on the business's WhatsApp account, as synced from Meta.

type WhatsAppTemplateChoice

type WhatsAppTemplateChoice struct {
	Name     string                 `json:"name,omitempty"`
	Language string                 `json:"language,omitempty"`
	Values   map[string]string      `json:"values,omitempty"`
	Submit   *WhatsAppTemplateDraft `json:"submit,omitempty"`
}

WhatsAppTemplateChoice is a template on the WhatsApp account by Name and Language, with Values, or a new one to Submit.

type WhatsAppTemplateDraft

type WhatsAppTemplateDraft struct {
	// Category is marketing or utility.
	Category TemplateCategory `json:"category"`
	// Language is "en" or "sw".
	Language     string   `json:"language"`
	Body         string   `json:"body"`
	Header       string   `json:"header,omitempty"`
	Footer       string   `json:"footer,omitempty"`
	QuickReplies []string `json:"quickReplies,omitempty"`
}

WhatsAppTemplateDraft is a new WhatsApp template, submitted to Meta for review.

type WhatsAppTemplateInput

type WhatsAppTemplateInput struct {
	ID     string            `json:"id"`
	Values map[string]string `json:"values,omitempty"`
}

WhatsAppTemplateInput is an approved WhatsApp template, with a value for each of its parameters.

type WhatsAppTemplateParameter

type WhatsAppTemplateParameter struct {
	Key     string `json:"key"`
	Example string `json:"example"`
}

WhatsAppTemplateParameter is a value a WhatsApp template needs, such as "body.1".

type WhatsAppTemplatesService

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

WhatsAppTemplatesService reads the templates on the WhatsApp account.

func (*WhatsAppTemplatesService) List

func (s *WhatsAppTemplatesService) List(ctx context.Context, approvedOnly bool) ([]WhatsAppTemplate, error)

List lists the templates on the WhatsApp account, by name. With approvedOnly, only the ones that can go now.

Jump to

Keyboard shortcuts

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