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.
Index ¶
- Constants
- type APIError
- type Activity
- type ActivityInput
- type ActivityRecordParams
- type ActivityService
- type AttachmentInput
- type AudienceInput
- type Broadcast
- type BroadcastAudience
- type BroadcastInput
- type BroadcastListParams
- type BroadcastSegment
- type BroadcastStatus
- type BroadcastsService
- func (s *BroadcastsService) Cancel(ctx context.Context, id string) (*Broadcast, error)
- func (s *BroadcastsService) Create(ctx context.Context, b BroadcastInput, opts ...RequestOption) (*Broadcast, error)
- func (s *BroadcastsService) Get(ctx context.Context, id string) (*Broadcast, error)
- func (s *BroadcastsService) List(ctx context.Context, params BroadcastListParams) iter.Seq2[Broadcast, error]
- func (s *BroadcastsService) ListPage(ctx context.Context, params BroadcastListParams) (*Page[Broadcast], error)
- type Channel
- type Client
- type ConnectionError
- type Content
- type Customer
- type CustomerActivityService
- func (s *CustomerActivityService) List(ctx context.Context, customerID string) iter.Seq2[Activity, error]
- func (s *CustomerActivityService) ListPage(ctx context.Context, customerID string, limit int, cursor string) (*Page[Activity], error)
- func (s *CustomerActivityService) Record(ctx context.Context, customerID string, activities []ActivityInput, ...) (*RecordedActivity, error)
- type CustomerCreateParams
- type CustomerInput
- type CustomerListParams
- type CustomerSegment
- type CustomerSegmentsService
- type CustomersService
- func (s *CustomersService) Create(ctx context.Context, params CustomerCreateParams, opts ...RequestOption) ([]SavedCustomer, error)
- func (s *CustomersService) Delete(ctx context.Context, id string) error
- func (s *CustomersService) Get(ctx context.Context, id string) (*Customer, error)
- func (s *CustomersService) List(ctx context.Context, params CustomerListParams) iter.Seq2[Customer, error]
- func (s *CustomersService) ListPage(ctx context.Context, params CustomerListParams) (*Page[Customer], error)
- func (s *CustomersService) Update(ctx context.Context, id string, changes CustomerInput) (*Customer, error)
- type FieldProblem
- type Media
- type Message
- type MessageFile
- type MessageInput
- type MessageListParams
- type MessageStatus
- type MessageTemplate
- type MessageTo
- type MessagesService
- func (s *MessagesService) Cancel(ctx context.Context, id string) (*Message, error)
- func (s *MessagesService) Get(ctx context.Context, id string) (*Message, error)
- func (s *MessagesService) List(ctx context.Context, params MessageListParams) iter.Seq2[Message, error]
- func (s *MessagesService) ListPage(ctx context.Context, params MessageListParams) (*Page[Message], error)
- func (s *MessagesService) Media(ctx context.Context, receivedID string) (*Media, error)
- func (s *MessagesService) Send(ctx context.Context, msgs []MessageInput, opts ...RequestOption) ([]Message, error)
- func (s *MessagesService) SendOne(ctx context.Context, msg MessageInput, opts ...RequestOption) (*Message, error)
- func (s *MessagesService) Senders(ctx context.Context) (*Senders, error)
- type NewCustomer
- type Option
- type Page
- type RecordedActivity
- type RequestOption
- type SavedCustomer
- type SavedWhatsAppTemplate
- type Segment
- type SegmentCondition
- type SegmentCustomersService
- func (s *SegmentCustomersService) Add(ctx context.Context, segmentID string, customerIDs []string) (*Segment, error)
- func (s *SegmentCustomersService) List(ctx context.Context, segmentID string) iter.Seq2[Customer, error]
- func (s *SegmentCustomersService) ListPage(ctx context.Context, segmentID string, limit int, cursor string) (*Page[Customer], error)
- func (s *SegmentCustomersService) Remove(ctx context.Context, segmentID string, customerIDs []string) (*Segment, error)
- type SegmentInput
- type SegmentRef
- type SegmentRules
- type SegmentsService
- func (s *SegmentsService) Create(ctx context.Context, segment SegmentInput) (*Segment, error)
- func (s *SegmentsService) Delete(ctx context.Context, id string) error
- func (s *SegmentsService) Get(ctx context.Context, id string) (*Segment, error)
- func (s *SegmentsService) List(ctx context.Context) ([]Segment, error)
- func (s *SegmentsService) Update(ctx context.Context, id string, changes SegmentInput) (*Segment, error)
- type Sender
- type SenderProvider
- type Senders
- type TemplateCategory
- type TemplateChoice
- type TemplateInput
- type TemplateListParams
- type TemplateRef
- type TemplateVariable
- type TemplatesService
- func (s *TemplatesService) Create(ctx context.Context, t TemplateInput, opts ...RequestOption) (*MessageTemplate, error)
- func (s *TemplatesService) Delete(ctx context.Context, id string) error
- func (s *TemplatesService) Get(ctx context.Context, id string) (*MessageTemplate, error)
- func (s *TemplatesService) List(ctx context.Context, params TemplateListParams) ([]MessageTemplate, error)
- func (s *TemplatesService) Update(ctx context.Context, id string, changes TemplateInput) (*MessageTemplate, error)
- type WebhookEndpoint
- type WebhookEndpointInput
- type WebhookEndpointsService
- func (s *WebhookEndpointsService) Create(ctx context.Context, endpoint WebhookEndpointInput) (*WebhookEndpoint, error)
- func (s *WebhookEndpointsService) Delete(ctx context.Context, id string) error
- func (s *WebhookEndpointsService) Get(ctx context.Context, id string) (*WebhookEndpoint, error)
- func (s *WebhookEndpointsService) List(ctx context.Context) ([]WebhookEndpoint, error)
- func (s *WebhookEndpointsService) Update(ctx context.Context, id string, changes WebhookEndpointInput) (*WebhookEndpoint, error)
- type WebhookEvent
- type WebhookVerificationError
- type WhatsAppService
- type WhatsAppTemplate
- type WhatsAppTemplateChoice
- type WhatsAppTemplateDraft
- type WhatsAppTemplateInput
- type WhatsAppTemplateParameter
- type WhatsAppTemplatesService
Constants ¶
const APIVersion = "2026-11-08"
APIVersion is the API version this release is written for. Every request is sent with it, as Goomzo-Version.
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.
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 ¶
func (s *ActivityService) Record(ctx context.Context, params ActivityRecordParams, opts ...RequestOption) (*RecordedActivity, error)
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 ¶
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) Create ¶
func (s *BroadcastsService) Create(ctx context.Context, b BroadcastInput, opts ...RequestOption) (*Broadcast, error)
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) List ¶
func (s *BroadcastsService) List(ctx context.Context, params BroadcastListParams) iter.Seq2[Broadcast, error]
List iterates every broadcast, newest first, asking for each page as you go.
func (*BroadcastsService) ListPage ¶
func (s *BroadcastsService) ListPage(ctx context.Context, params BroadcastListParams) (*Page[Broadcast], error)
ListPage gets one page of broadcasts, newest first.
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.
type ConnectionError ¶
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.
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 ¶
func (s *CustomersService) Create(ctx context.Context, params CustomerCreateParams, opts ...RequestOption) ([]SavedCustomer, error)
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) List ¶
func (s *CustomersService) List(ctx context.Context, params CustomerListParams) iter.Seq2[Customer, error]
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 ¶
FieldProblem is a problem with one field of a request.
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 ¶
Cancel cancels a scheduled message before it goes. A message already sending can't be canceled.
func (*MessagesService) List ¶
func (s *MessagesService) List(ctx context.Context, params MessageListParams) iter.Seq2[Message, error]
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 ¶
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."},
})
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 ¶
WithAPIKey sets your API key's secret, gzk_…. Left out, GOOMZO_API_KEY is used. Keep it on your server.
func WithBaseURL ¶
WithBaseURL sets where the API is. Left out, DefaultBaseURL.
func WithHTTPClient ¶
WithHTTPClient sets your own *http.Client, such as for a proxy or tests.
func WithMaxRetries ¶
WithMaxRetries sets how many times a request is tried again after a network error, a 429 or a 5xx. Left out, 2.
func WithTimeout ¶
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 ¶
func (s *SegmentCustomersService) List(ctx context.Context, segmentID string) iter.Seq2[Customer, error]
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)
}
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 ¶
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) 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"`
// 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 ¶
func (s *TemplatesService) Create(ctx context.Context, t TemplateInput, opts ...RequestOption) (*MessageTemplate, error)
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 ¶
func (s *TemplatesService) Get(ctx context.Context, id string) (*MessageTemplate, error)
Get gets a template by its id.
func (*TemplatesService) List ¶
func (s *TemplatesService) List(ctx context.Context, params TemplateListParams) ([]MessageTemplate, error)
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 ¶
func (s *WebhookEndpointsService) Create(ctx context.Context, endpoint WebhookEndpointInput) (*WebhookEndpoint, error)
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 ¶
func (s *WebhookEndpointsService) Get(ctx context.Context, id string) (*WebhookEndpoint, error)
Get gets an endpoint by its id.
func (*WebhookEndpointsService) List ¶
func (s *WebhookEndpointsService) List(ctx context.Context) ([]WebhookEndpoint, error)
List lists the endpoints.
func (*WebhookEndpointsService) Update ¶
func (s *WebhookEndpointsService) Update(ctx context.Context, id string, changes WebhookEndpointInput) (*WebhookEndpoint, error)
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 ¶
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"`
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"`
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 ¶
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.