opensms

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Sep 24, 2026 License: MIT Imports: 18 Imported by: 0

README

opensms-go (Go)

Official Go client for opensms: prepaid SMS for Africa.

Requires Go 1.21+. Standard library only, no dependencies. See the monorepo overview for what every language client shares, and spec/SURFACE.md for the full method and field list this client wraps.

Install

go get github.com/opensms-io/opensms-go

Usage

package main

import (
	"context"
	"log"

	opensms "github.com/opensms-io/opensms-go"
)

func main() {
	client, err := opensms.NewClient("sk_test_...")
	if err != nil {
		log.Fatal(err) // the key is malformed; no request was made
	}

	msg, err := client.Messages.Send(context.Background(), opensms.SendMessageParams{
		To:   "+254712345678",
		Text: "Your order has shipped",
	})
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("sent %s (%s)", msg.ID, msg.Status)
}

sk_test_ keys run against the sandbox (free, mock delivery); sk_live_ keys send real traffic. The key alone selects the workspace and environment; client.Environment() returns sandbox or live.

More

Money, prices and balances decode as decimal strings, never floats. Timestamps decode to time.Time. Optional pointer fields take the opensms.Bool and opensms.Int helpers.

Messages
msg, err := client.Messages.Send(ctx, opensms.SendMessageParams{To: "+254712345678", Text: "Your code is ready"})
page, err := client.Messages.List(ctx, opensms.ListMessagesParams{Limit: 50, Status: "delivered"})
msg, err = client.Messages.Get(ctx, msg.ID)
attempts, err := client.Messages.Attempts(ctx, msg.ID)
msg, err = client.Messages.Cancel(ctx, msg.ID) // queued or scheduled only, never retried
Batches
b, err := client.Batches.Create(ctx, opensms.CreateBatchParams{Items: []opensms.BatchItemInput{
	{To: "+254712345678", Text: "Hello Ada"},
}})
b, err = client.Batches.Start(ctx, b.ID) // batches send nothing until started
items, err := client.Batches.ListItems(ctx, b.ID, opensms.ListBatchItemsParams{})
OTP
res, err := client.OTP.Send(ctx, opensms.SendOTPParams{To: "+254712345678", Length: 6, TTLSeconds: 300})
v, err := client.OTP.Verify(ctx, opensms.VerifyOTPParams{OTPID: res.OTPID, Code: "123456"})
if v.Valid { /* ... */ } // a wrong code returns Valid false and uses an attempt, never retried
Lookups
l, err := client.Lookups.Create(ctx, opensms.CreateLookupParams{To: "+254712345678"})
l, err = client.Lookups.Get(ctx, l.ID) // 202 on create means still pending: poll Get
Contacts
c, err := client.Contacts.Create(ctx, opensms.CreateContactParams{E164: "+254712345678", Name: "Ada"})
c, err = client.Contacts.Update(ctx, c.ID, opensms.UpdateContactParams{Name: "Ada L"})
page, err := client.Contacts.List(ctx, opensms.ListParams{Limit: 100})
err = client.Contacts.Delete(ctx, c.ID)
Contact groups
g, err := client.ContactGroups.Create(ctx, opensms.CreateContactGroupParams{Name: "VIP", ContactIDs: []string{c.ID}})
batch, err := client.ContactGroups.Send(ctx, g.ID, opensms.GroupSendParams{Text: "Hi from OpenSMS"})
err = client.ContactGroups.Delete(ctx, g.ID)
Templates
tpl, err := client.Templates.Create(ctx, opensms.CreateTemplateParams{Name: "welcome", Body: "Hi {{name}}"})
batch, err := client.ContactGroups.Send(ctx, g.ID, opensms.GroupSendParams{
	TemplateID: tpl.ID, Variables: map[string]string{"name": "Ada"},
})
Webhooks
wh, err := client.Webhooks.Create(ctx, opensms.CreateWebhookParams{
	URL:    "https://example.com/opensms",
	Events: []string{"message.delivered", "message.failed"},
})
secret := wh.Secret // whsec_..., returned only once: store it
deliveries, err := client.Webhooks.ListDeliveries(ctx, wh.ID, opensms.ListParams{})

See Webhooks below for verifying incoming deliveries.

Inbound
page, err := client.Inbound.List(ctx, opensms.ListParams{})
reply, err := client.Inbound.Reply(ctx, page.Items[0].ID, opensms.InboundReplyParams{Text: "Thanks!"}) // live keys only
Numbers

List and Available work with any key; the rest need a live key.

avail, err := client.Numbers.Available(ctx, opensms.AvailableNumbersParams{Country: "KE", Kind: "long_code"})
n, err := client.Numbers.Assign(ctx, opensms.AssignNumberParams{Country: "KE", Kind: "long_code"}) // charges the wallet
rule, err := client.Numbers.CreateRule(ctx, n.ID, opensms.NumberRuleParams{
	Match: "keyword", Pattern: "HELP", Action: "auto_reply", Target: "Reply STOP to opt out",
})
Sender IDs
quote, err := client.SenderIDs.Quote(ctx, opensms.QuoteSenderIDParams{Countries: []string{"KE", "NG"}})
s, err := client.SenderIDs.Create(ctx, opensms.CreateSenderIDParams{
	Value: "ACME", Kind: "alphanumeric", Countries: []string{"KE"},
	UseCase: "transactional", QuoteID: quote.QuoteID,
}) // may charge fees (see Quote), never retried
Suppressions
s, err := client.Suppressions.Create(ctx, opensms.CreateSuppressionParams{E164: "+254712345678", Reason: "manual"}) // never retried
res, err := client.Suppressions.Import(ctx, []opensms.SuppressionInput{{E164: "+254712345699", Reason: "complaint"}}) // never retried
err = client.Suppressions.Delete(ctx, s.ID)
Compliance
ke, err := client.Compliance.GetCountry(ctx, "KE") // stop keywords, quiet hours, content rules
all, err := client.Compliance.ListCountries(ctx)
Wallet
balances, err := client.Wallet.Balances(ctx)
entries, err := client.Wallet.Ledger(ctx, opensms.LedgerParams{Limit: opensms.Int(50)})
// Ledger pages with Before (the smallest id seen), not cursors.
older, err := client.Wallet.Ledger(ctx, opensms.LedgerParams{Limit: opensms.Int(50), Before: entries[len(entries)-1].ID})
Pricing
prices, err := client.Pricing.Get(ctx, opensms.PricingParams{Product: "sms", Country: "KE"})
Analytics
ov, err := client.Analytics.Overview(ctx, opensms.AnalyticsParams{Range: "7d"})
byCountry, err := client.Analytics.ByCountry(ctx, opensms.AnalyticsParams{})
series, err := client.Analytics.Timeseries(ctx, opensms.AnalyticsParams{Bucket: "day"})
Sandbox
page, err := client.Sandbox.ListMessages(ctx, opensms.ListParams{Limit: 10}) // rendered text, including OTP codes
Countries
countries, err := client.Countries.List(ctx)
carriers, err := client.Countries.Carriers(ctx, "KE")
Pagination

Cursor lists return *opensms.Page[T] with Items and NextCursor (empty on the last page). Paginate walks every page lazily:

it := opensms.Paginate(ctx, client.Messages.List, opensms.ListMessagesParams{Limit: 100})
for it.Next() {
	m := it.Item()
	_ = m
}
if err := it.Err(); err != nil {
	log.Fatal(err)
}

For a list method that takes an id (Batches.ListItems, Webhooks.ListDeliveries, Numbers.ListRules), wrap the call in a closure that captures it.

Errors and retries

Every non-2xx response, and every transport failure that survives all retries, is a *opensms.Error, mapped from the RFC 9457 problem+json body:

_, err := client.Messages.Send(ctx, params)
var oe *opensms.Error
if errors.As(err, &oe) {
	log.Printf("status=%d detail=%q code=%q request=%q", oe.Status, oe.Detail, oe.Code, oe.RequestID)
}
Field Meaning
Status HTTP status; 0 means no response (network failure or timeout)
Type, Title, Detail problem fields; Detail is the human text
Code machine code, when present
TraceID, Errors trace id and field validation errors, when present
RequestID X-Request-ID, set on message and OTP admission rejections
RetryAfter the Retry-After header, when present
Body raw response body

Most errors carry no Code, so branch on Status and show Detail. Insufficient scope is 401 on Messages and OTP but 403 on every other resource.

Errors raised before any request (a malformed API key in NewClient, an empty id) wrap opensms.ErrInvalidArgument; test them with errors.Is.

Retries: 429, 500, 502, 503, 504, network errors and timeouts are retried with exponential backoff and full jitter, capped at 8s. GET, PUT, PATCH and DELETE are always retryable; POST only when it carries an Idempotency-Key, which the SDK generates once per call (a UUIDv4) and reuses unchanged on every retry of that call. A Retry-After header (seconds or an HTTP date) is honoured; if it asks for more than 60s the SDK does not wait, it returns the error with RetryAfter set instead. Other 4xx responses are never retried, and neither are Messages.Cancel, OTP.Verify, SenderIDs.Create, SenderIDs.CreateDraft, Suppressions.Create and Suppressions.Import.

Webhooks

Each delivery carries X-OpenSMS-Signature: t=<unix>,v1=<hex>, HMAC-SHA256 of "<t>.<raw body>" keyed with the endpoint's whsec_... secret, used verbatim. Verify the raw body before parsing it; no client or API key is needed:

func handler(w http.ResponseWriter, r *http.Request) {
	body, _ := io.ReadAll(r.Body)
	event, err := opensms.ConstructEvent(body, r.Header.Get(opensms.SignatureHeader), os.Getenv("OPENSMS_WEBHOOK_SECRET"))
	if err != nil {
		http.Error(w, "bad signature", http.StatusBadRequest) // Code is invalid_signature or expired_signature
		return
	}
	log.Printf("%s %v", event.Type, event.Data["status"])
	w.WriteHeader(http.StatusNoContent)
}

opensms.VerifySignature(body, header, secret) returns a plain bool instead. The default tolerance is 300 seconds; override it with opensms.VerifyOptions{Tolerance: 10 * time.Minute}.

Not covered

Account, team and key management, workspace settings, billing documents, onboarding and sender document upload are console-only (session auth) and are not in this SDK. The realtime WebSocket stream is not wrapped either.

Testing

Unit tests need no network:

go test ./...

The live suite runs the CONFORMANCE.md scenario against a sandbox. It runs only when OPENSMS_BASE_URL and OPENSMS_API_KEY are set, and is skipped otherwise:

go test -run TestLiveConformance -v ./...

License

MIT

Documentation

Overview

Package opensms is the official Go client for the OpenSMS prepaid SMS API.

Construct a client with an API key (sk_test_ for the sandbox, sk_live_ for live traffic) and reach the API through resource fields:

client, err := opensms.NewClient("sk_test_...")
if err != nil {
	log.Fatal(err)
}
msg, err := client.Messages.Send(ctx, opensms.SendMessageParams{
	To:   "+254700000012",
	Text: "Your order has shipped",
})

One transport (transport.go) owns bearer authentication, JSON encoding, Idempotency-Key generation (one UUID per call, reused on every retry), retries on 429 and 5xx with backoff that honours Retry-After, and mapping of problem+json errors to *Error. Resources are thin wrappers over it. Every method takes a context.Context as its first argument.

Cursor lists return a *Page[T]; Paginate walks every page lazily. VerifySignature and ConstructEvent check webhook signatures without a client or API key.

Index

Constants

View Source
const (
	// Version is the SDK version, sent in the User-Agent header.
	Version = "0.1.1"

	// DefaultBaseURL is the production API origin.
	DefaultBaseURL = "https://opensms.io"
)
View Source
const DefaultSignatureTolerance = 300 * time.Second

DefaultSignatureTolerance is the default allowed clock skew.

View Source
const SignatureHeader = "X-OpenSMS-Signature"

SignatureHeader is the header that carries a webhook delivery signature.

Variables

View Source
var ErrInvalidArgument = errors.New("opensms: invalid argument")

ErrInvalidArgument is wrapped by errors returned before any request is made (an invalid API key in NewClient, an empty id). Test with errors.Is.

Functions

func Bool

func Bool(v bool) *bool

Bool returns a pointer to v, for optional boolean parameters.

func Int

func Int(v int) *int

Int returns a pointer to v, for optional integer parameters.

func VerifySignature

func VerifySignature(payload []byte, header, secret string, opts ...VerifyOptions) bool

VerifySignature reports whether header (the X-OpenSMS-Signature value) is a valid signature of payload, the exact raw request body, for the endpoint secret. The secret is used verbatim, including its whsec_ prefix. It needs no API key or client.

Types

type Analytics

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

Analytics reads delivery and spend metrics, reached as client.Analytics.

func (*Analytics) ByCarrier

func (r *Analytics) ByCarrier(ctx context.Context, params AnalyticsParams) ([]AnalyticsBreakdown, error)

ByCarrier breaks metrics down by carrier (GET /v1/analytics/by-carrier).

func (*Analytics) ByCountry

func (r *Analytics) ByCountry(ctx context.Context, params AnalyticsParams) ([]AnalyticsBreakdown, error)

ByCountry breaks metrics down by country (GET /v1/analytics/by-country).

func (*Analytics) BySenderID

func (r *Analytics) BySenderID(ctx context.Context, params AnalyticsParams) ([]AnalyticsBreakdown, error)

BySenderID breaks metrics down by sender ID (GET /v1/analytics/by-sender-id).

func (*Analytics) Overview

func (r *Analytics) Overview(ctx context.Context, params AnalyticsParams) (*AnalyticsOverview, error)

Overview returns totals for the period (GET /v1/analytics/overview).

func (*Analytics) Timeseries

func (r *Analytics) Timeseries(ctx context.Context, params AnalyticsParams) ([]AnalyticsPoint, error)

Timeseries returns metrics per bucket (GET /v1/analytics/timeseries).

type AnalyticsBreakdown

type AnalyticsBreakdown struct {
	Metrics
	Key  string `json:"key"`
	Name string `json:"name"`
}

AnalyticsBreakdown is one row of a by-country, by-carrier or by-sender-id breakdown.

type AnalyticsOverview

type AnalyticsOverview struct {
	Metrics
	From        time.Time `json:"from"`
	To          time.Time `json:"to"`
	Currency    string    `json:"currency"`
	Environment string    `json:"environment"`
}

AnalyticsOverview is returned by Analytics.Overview.

type AnalyticsParams

type AnalyticsParams struct {
	// Currency is a 3-letter code (default: the workspace currency).
	Currency string
	// Range is Nd with N in 1..366 (default 30d).
	Range string
	From  time.Time
	To    time.Time
	// Bucket is day or hour.
	Bucket string
}

AnalyticsParams is the common analytics query. Use Range or From/To, not both.

type AnalyticsPoint

type AnalyticsPoint struct {
	Metrics
	Bucket time.Time `json:"bucket"`
}

AnalyticsPoint is one timeseries bucket.

type AssignNumberParams

type AssignNumberParams struct {
	Country string `json:"country"`
	Kind    string `json:"kind"`
}

AssignNumberParams is the body for Numbers.Assign.

type Attempt

type Attempt struct {
	ID                int64      `json:"id"`
	Sequence          int        `json:"sequence"`
	RouteID           string     `json:"route_id"`
	RouteName         string     `json:"route_name"`
	Price             string     `json:"price"`
	Currency          string     `json:"currency"`
	Provider          string     `json:"provider"`
	ProviderMessageID string     `json:"provider_message_id"`
	Status            string     `json:"status"`
	ErrorCode         string     `json:"error_code"`
	SubmittedAt       *time.Time `json:"submitted_at"`
	DLRAt             *time.Time `json:"dlr_at"`
	SubmitLatencyMs   *int64     `json:"submit_latency_ms"`
	DLRLatencyMs      *int64     `json:"dlr_latency_ms"`
}

Attempt is one provider submission of a message.

type AvailableNumbersParams

type AvailableNumbersParams struct {
	Country string
	// Kind is long_code, short_code or toll_free.
	Kind string
}

AvailableNumbersParams filters Numbers.Available.

type Batch

type Batch struct {
	ID         string `json:"id"`
	Status     string `json:"status"`
	Total      int    `json:"total"`
	Sent       int    `json:"sent"`
	Delivered  int    `json:"delivered"`
	Failed     int    `json:"failed"`
	Invalid    int    `json:"invalid"`
	Duplicates int    `json:"duplicates"`
	Suppressed int    `json:"suppressed"`
	// EstimatedCost is the decimal estimate, empty when null.
	EstimatedCost json.Number `json:"estimated_cost"`
	CreatedAt     time.Time   `json:"created_at"`
	CompletedAt   *time.Time  `json:"completed_at"`
}

Batch is a bulk send.

type BatchItem

type BatchItem = Message

BatchItem is a message created by a batch. The API returns only id, created_at, to, text, sender_id, parts, status, traffic_type and metadata.

type BatchItemInput

type BatchItemInput struct {
	To          string         `json:"to"`
	Text        string         `json:"text"`
	SenderID    string         `json:"sender_id,omitempty"`
	TrafficType string         `json:"traffic_type,omitempty"`
	CallbackURL string         `json:"callback_url,omitempty"`
	Metadata    map[string]any `json:"metadata,omitempty"`
}

BatchItemInput is one message in a batch.

type BatchStopResult

type BatchStopResult struct {
	ID        string `json:"id"`
	Status    string `json:"status"`
	Cancelled int    `json:"cancelled"`
}

BatchStopResult is returned by Batches.Stop.

type Batches

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

Batches is the batches resource, reached as client.Batches. A batch is created in the ready state and sends nothing until Start.

func (*Batches) Create

func (r *Batches) Create(ctx context.Context, params CreateBatchParams, opts ...CallOption) (*Batch, error)

Create creates a batch from JSON items (POST /v1/messages/batch). Invalid rows do not fail the request; they are counted and listed by Validation.

func (*Batches) CreateFromCSV

func (r *Batches) CreateFromCSV(ctx context.Context, csv []byte, params CreateBatchFromCSVParams, opts ...CallOption) (*Batch, error)

CreateFromCSV creates a batch from CSV text with a to,text[,sender_id,...] header row (POST /v1/messages/batch, Content-Type text/csv). When params.Dedupe is set, the CSV is sent as a multipart upload with a dedupe field, the only form in which the API accepts that flag for CSV.

func (*Batches) Get

func (r *Batches) Get(ctx context.Context, id string) (*Batch, error)

Get fetches a batch (GET /v1/batches/{id}).

func (*Batches) ListItems

func (r *Batches) ListItems(ctx context.Context, id string, params ListBatchItemsParams) (*Page[BatchItem], error)

ListItems returns a page of the messages a batch created (GET /v1/batches/{id}/items).

func (*Batches) Start

func (r *Batches) Start(ctx context.Context, id string, opts ...CallOption) (*Batch, error)

Start starts a ready batch (POST /v1/batches/{id}/start).

func (*Batches) Stop

func (r *Batches) Stop(ctx context.Context, id string, opts ...CallOption) (*BatchStopResult, error)

Stop stops a batch and cancels its unsent items (POST /v1/batches/{id}/stop).

func (*Batches) Validation

func (r *Batches) Validation(ctx context.Context, id string) (*ValidationReport, error)

Validation returns the per-row validation report (GET /v1/batches/{id}/validation).

type CallOption

type CallOption func(*callOptions)

CallOption configures a single method call.

func WithIdempotencyKey

func WithIdempotencyKey(key string) CallOption

WithIdempotencyKey sets the Idempotency-Key for a call. Without it, methods that support idempotency send a generated UUIDv4. The same key is reused on every retry of that call. Ignored by methods that do not send the header.

type Carrier

type Carrier struct {
	ID       string   `json:"id"`
	Name     string   `json:"name"`
	MCCMNC   []string `json:"mcc_mnc"`
	Prefixes []string `json:"prefixes"`
}

Carrier is a mobile network in a country.

type CheckSenderIDParams

type CheckSenderIDParams struct {
	Value   string
	Country string
}

CheckSenderIDParams is the query for SenderIDs.Check.

type Client

type Client struct {
	// Messages sends, lists, inspects and cancels SMS messages.
	Messages *Messages
	// Batches creates and runs bulk sends.
	Batches *Batches
	// OTP sends and verifies one-time passcodes.
	OTP *OTP
	// Lookups runs number lookups.
	Lookups *Lookups
	// Contacts manages the contact book.
	Contacts *Contacts
	// ContactGroups manages contact groups and sends to them.
	ContactGroups *ContactGroups
	// Templates manages reusable message templates.
	Templates *Templates
	// Webhooks manages webhook endpoints and deliveries, and verifies
	// signatures.
	Webhooks *Webhooks
	// Inbound lists and replies to inbound messages.
	Inbound *Inbound
	// Numbers manages virtual numbers and their inbound rules.
	Numbers *Numbers
	// SenderIDs manages sender IDs, drafts and documents.
	SenderIDs *SenderIDs
	// Suppressions manages the do-not-send list.
	Suppressions *Suppressions
	// Compliance reads country rules and content rules.
	Compliance *Compliance
	// Wallet reads balances and the ledger and creates top-ups.
	Wallet *Wallet
	// Pricing reads the workspace price list.
	Pricing *Pricing
	// Analytics reads delivery and spend metrics.
	Analytics *Analytics
	// Sandbox lists rendered sandbox messages.
	Sandbox *Sandbox
	// Countries reads the public country catalog.
	Countries *Countries
	// contains filtered or unexported fields
}

Client is the OpenSMS API client. Construct it with NewClient and reach the API through its resource fields. Client is safe for concurrent use.

func NewClient

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

NewClient constructs a Client. apiKey must start with sk_test_ (sandbox) or sk_live_ (live) and have more than 12 characters after the prefix; anything else returns an error wrapping ErrInvalidArgument without any network call.

func (*Client) Environment

func (c *Client) Environment() string

Environment returns "sandbox" for an sk_test_ key and "live" for sk_live_.

type Compliance

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

Compliance reads country and content rules, reached as client.Compliance.

func (*Compliance) GetCountry

func (r *Compliance) GetCountry(ctx context.Context, iso2 string) (*CountryRules, error)

GetCountry returns one country's rules (GET /v1/compliance/countries/{iso2}).

func (*Compliance) ListContentRules

func (r *Compliance) ListContentRules(ctx context.Context) ([]ContentRule, error)

ListContentRules returns the platform content rules (GET /v1/content-rules).

func (*Compliance) ListCountries

func (r *Compliance) ListCountries(ctx context.Context) ([]CountryRules, error)

ListCountries returns the rules for every country (GET /v1/compliance/countries).

type Contact

type Contact struct {
	ID          string         `json:"id"`
	WorkspaceID string         `json:"workspace_id"`
	E164        string         `json:"e164"`
	Name        string         `json:"name"`
	Attributes  map[string]any `json:"attributes"`
	CreatedAt   time.Time      `json:"created_at"`
}

Contact is an address-book entry.

type ContactGroup

type ContactGroup struct {
	ID          string    `json:"id"`
	WorkspaceID string    `json:"workspace_id"`
	Name        string    `json:"name"`
	ContactIDs  []string  `json:"contact_ids"`
	CreatedAt   time.Time `json:"created_at"`
}

ContactGroup is a named set of contacts.

type ContactGroups

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

ContactGroups manages contact groups, reached as client.ContactGroups.

func (*ContactGroups) Create

Create creates a group (POST /v1/contact-groups).

func (*ContactGroups) Delete

func (r *ContactGroups) Delete(ctx context.Context, id string) error

Delete deletes a group (DELETE /v1/contact-groups/{id}).

func (*ContactGroups) Get

func (r *ContactGroups) Get(ctx context.Context, id string) (*ContactGroup, error)

Get fetches a group (GET /v1/contact-groups/{id}).

func (*ContactGroups) List

func (r *ContactGroups) List(ctx context.Context, params ListParams) (*Page[ContactGroup], error)

List returns a page of groups (GET /v1/contact-groups).

func (*ContactGroups) Send

func (r *ContactGroups) Send(ctx context.Context, id string, params GroupSendParams, opts ...CallOption) (*Batch, error)

Send sends text or a template to every member and returns the running batch (POST /v1/contact-groups/{id}/send).

func (*ContactGroups) Update

Update changes a group's name or members (PATCH /v1/contact-groups/{id}).

type Contacts

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

Contacts is the contact book, reached as client.Contacts.

func (*Contacts) Create

func (r *Contacts) Create(ctx context.Context, params CreateContactParams, opts ...CallOption) (*Contact, error)

Create creates a contact (POST /v1/contacts). A duplicate e164 returns 409.

func (*Contacts) Delete

func (r *Contacts) Delete(ctx context.Context, id string) error

Delete deletes a contact (DELETE /v1/contacts/{id}).

func (*Contacts) Get

func (r *Contacts) Get(ctx context.Context, id string) (*Contact, error)

Get fetches a contact (GET /v1/contacts/{id}).

func (*Contacts) List

func (r *Contacts) List(ctx context.Context, params ListParams) (*Page[Contact], error)

List returns a page of contacts (GET /v1/contacts).

func (*Contacts) Update

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

Update changes the given fields of a contact (PATCH /v1/contacts/{id}).

type ContentRule

type ContentRule struct {
	ID           int64    `json:"id"`
	CountryISO2  string   `json:"country_iso2"`
	Kind         string   `json:"kind"`
	Pattern      string   `json:"pattern"`
	Action       string   `json:"action"`
	TrafficTypes []string `json:"traffic_types"`
	Enabled      bool     `json:"enabled"`
}

ContentRule is a platform content rule.

type Countries

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

Countries reads the public country catalog, reached as client.Countries.

func (*Countries) Carriers

func (r *Countries) Carriers(ctx context.Context, iso2 string) ([]Carrier, error)

Carriers returns a country's carriers (GET /v1/countries/{iso2}/carriers).

func (*Countries) Compliance

func (r *Countries) Compliance(ctx context.Context, iso2 string) (*CountryRules, error)

Compliance returns a country's compliance rules (GET /v1/countries/{iso2}/compliance).

func (*Countries) List

func (r *Countries) List(ctx context.Context) ([]Country, error)

List returns the active countries (GET /v1/countries).

func (*Countries) Routes

func (r *Countries) Routes(ctx context.Context, iso2 string) ([]Route, error)

Routes returns a country's routable routes (GET /v1/countries/{iso2}/routes).

type Country

type Country struct {
	ISO2               string   `json:"iso2"`
	Name               string   `json:"name"`
	DialCode           string   `json:"dial_code"`
	Currency           string   `json:"currency"`
	Status             string   `json:"status"`
	PricePerMessage    *Money   `json:"price_per_message"`
	SenderKinds        []string `json:"sender_kinds"`
	ProvidersAvailable int      `json:"providers_available"`
}

Country is a public catalog country.

type CountryContentRule

type CountryContentRule struct {
	Kind         string   `json:"kind"`
	Pattern      string   `json:"pattern"`
	Action       string   `json:"action"`
	TrafficTypes []string `json:"traffic_types"`
	Enabled      bool     `json:"enabled"`
}

CountryContentRule is a content rule embedded in CountryRules.

type CountryRules

type CountryRules struct {
	ISO2         string               `json:"iso2"`
	Name         string               `json:"name"`
	Status       string               `json:"status"`
	DialCode     string               `json:"dial_code"`
	StopKeywords []string             `json:"stop_keywords"`
	QuietHours   []QuietHours         `json:"quiet_hours"`
	ContentRules []CountryContentRule `json:"content_rules"`
}

CountryRules are the compliance rules for one country.

type CreateBatchFromCSVParams

type CreateBatchFromCSVParams struct {
	// Dedupe drops duplicate destinations (server default true). When set,
	// the CSV is sent as a multipart upload so the flag can travel with it.
	Dedupe *bool
}

CreateBatchFromCSVParams configures Batches.CreateFromCSV.

type CreateBatchParams

type CreateBatchParams struct {
	Items []BatchItemInput `json:"items"`
	// Dedupe drops duplicate destinations (server default true).
	Dedupe *bool `json:"dedupe,omitempty"`
}

CreateBatchParams is the body for Batches.Create.

type CreateContactGroupParams

type CreateContactGroupParams struct {
	Name       string   `json:"name"`
	ContactIDs []string `json:"contact_ids,omitempty"`
}

CreateContactGroupParams is the body for ContactGroups.Create.

type CreateContactParams

type CreateContactParams struct {
	E164       string         `json:"e164"`
	Name       string         `json:"name,omitempty"`
	Attributes map[string]any `json:"attributes,omitempty"`
}

CreateContactParams is the body for Contacts.Create.

type CreateLookupParams

type CreateLookupParams struct {
	To string `json:"to"`
}

CreateLookupParams is the body for Lookups.Create.

type CreateSenderIDParams

type CreateSenderIDParams struct {
	Value     string   `json:"value"`
	Kind      string   `json:"kind"`
	Countries []string `json:"countries"`
	UseCase   string   `json:"use_case,omitempty"`
	// SampleMessage is an example of what will be sent.
	SampleMessage string `json:"sample_message,omitempty"`
	// Documents are sender document ids (always sent, may be empty).
	Documents    []string `json:"documents"`
	DraftID      string   `json:"draft_id,omitempty"`
	DraftVersion *int     `json:"draft_version,omitempty"`
	QuoteID      string   `json:"quote_id,omitempty"`
}

CreateSenderIDParams is the body for SenderIDs.Create.

type CreateSuppressionParams

type CreateSuppressionParams = SuppressionInput

CreateSuppressionParams is the body for Suppressions.Create.

type CreateTemplateParams

type CreateTemplateParams struct {
	Name        string `json:"name"`
	Body        string `json:"body"`
	TrafficType string `json:"traffic_type,omitempty"`
}

CreateTemplateParams is the body for Templates.Create.

type CreateTopupParams

type CreateTopupParams struct {
	// Amount is a decimal string.
	Amount   string `json:"amount"`
	Currency string `json:"currency"`
	// Channel is card, mobile_money or bank_transfer.
	Channel string `json:"channel"`
	Email   string `json:"email"`
}

CreateTopupParams is the body for Wallet.CreateTopup.

type CreateWebhookParams

type CreateWebhookParams struct {
	// URL must be https without credentials or fragment.
	URL    string   `json:"url"`
	Events []string `json:"events"`
	// Enabled defaults to true.
	Enabled *bool `json:"enabled,omitempty"`
}

CreateWebhookParams is the body for Webhooks.Create.

type Cursorable

type Cursorable[P any] interface {
	WithCursor(cursor string) P
}

Cursorable is implemented by list parameter types. WithCursor returns a copy of the parameters with Cursor set.

type Error

type Error struct {
	// Status is the HTTP status code. 0 means no response was received
	// (network failure, timeout) or a local failure such as an invalid
	// webhook signature.
	Status int
	// Type is the problem type URI, usually "about:blank", otherwise
	// "https://api.opensms.io/problems/<code>".
	Type string
	// Title is the short problem title ("Bad Request", "Unauthorized", ...).
	Title string
	// Detail is the human-readable explanation.
	Detail string
	// Code is the machine-readable code, when the handler sets one
	// ("invalid_message_id", "not_found", "invalid_signature", ...).
	Code string
	// TraceID is the problem trace_id, when present.
	TraceID string
	// Errors holds field validation errors, when present.
	Errors map[string][]string
	// RequestID is the X-Request-ID response header, set on message and OTP
	// admission rejections (for example 422 destination is suppressed).
	RequestID string
	// RetryAfter is the Retry-After header value, or 0 when absent.
	RetryAfter time.Duration
	// Body is the raw response body (JSON or text), for debugging.
	Body []byte
	// contains filtered or unexported fields
}

Error is returned for every non-2xx API response, for transport failures that survive all retries, and for webhook signature failures. It is mapped from the RFC 9457 problem+json body the API returns.

Most OpenSMS errors carry no Code, so branch on Status first and use Detail for display. Insufficient scope is 401 on messages and otp but 403 on every other resource.

func (*Error) Error

func (e *Error) Error() string

Error implements the error interface.

func (*Error) Message

func (e *Error) Message() string

Message returns Detail, else Title, else a generic message with the status.

func (*Error) Unwrap

func (e *Error) Unwrap() error

Unwrap returns the underlying transport error, if any.

type GroupSendParams

type GroupSendParams struct {
	Text        string            `json:"text,omitempty"`
	TemplateID  string            `json:"template_id,omitempty"`
	Variables   map[string]string `json:"variables,omitempty"`
	SenderID    string            `json:"sender_id,omitempty"`
	TrafficType string            `json:"traffic_type,omitempty"`
	CallbackURL string            `json:"callback_url,omitempty"`
}

GroupSendParams is the body for ContactGroups.Send. Set Text or TemplateID.

type Inbound

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

Inbound lists and answers inbound messages, reached as client.Inbound.

func (*Inbound) List

func (r *Inbound) List(ctx context.Context, params ListParams) (*Page[InboundMessage], error)

List returns a page of inbound messages (GET /v1/inbound).

func (*Inbound) Reply

func (r *Inbound) Reply(ctx context.Context, id string, params InboundReplyParams, opts ...CallOption) (*Message, error)

Reply answers an inbound message and returns the outbound Message (POST /v1/inbound/{id}/reply). Live keys only.

type InboundMessage

type InboundMessage struct {
	ID              string    `json:"id"`
	From            string    `json:"from"`
	To              string    `json:"to"`
	Text            string    `json:"text"`
	ReceivedAt      time.Time `json:"received_at"`
	VirtualNumberID string    `json:"virtual_number_id"`
}

InboundMessage is an SMS received on a virtual number.

type InboundReplyParams

type InboundReplyParams struct {
	Text string `json:"text"`
}

InboundReplyParams is the body for Inbound.Reply.

type Iterator

type Iterator[T any, P Cursorable[P]] struct {
	// contains filtered or unexported fields
}

Iterator walks every item of a cursor-paginated list, fetching pages lazily. Use it as:

it := opensms.Paginate(ctx, client.Messages.List, opensms.ListMessagesParams{Limit: 50})
for it.Next() {
	m := it.Item()
}
if err := it.Err(); err != nil { ... }

func Paginate

func Paginate[T any, P Cursorable[P]](ctx context.Context, list func(context.Context, P) (*Page[T], error), params P) *Iterator[T, P]

Paginate returns an Iterator over list, starting from params and feeding each page's NextCursor back as the cursor until it is empty. For list methods that take an id (Batches.ListItems, Webhooks.ListDeliveries, Numbers.ListRules), wrap the call in a closure that captures the id.

func (*Iterator[T, P]) Err

func (it *Iterator[T, P]) Err() error

Err returns the first error encountered, if any.

func (*Iterator[T, P]) Item

func (it *Iterator[T, P]) Item() T

Item returns the current item.

func (*Iterator[T, P]) Next

func (it *Iterator[T, P]) Next() bool

Next advances to the next item, fetching a page when needed. It returns false at the end of the list or on error.

type LedgerEntry

type LedgerEntry struct {
	ID            int64     `json:"id"`
	WalletID      string    `json:"wallet_id"`
	Type          string    `json:"type"`
	Amount        string    `json:"amount"`
	BalanceAfter  string    `json:"balance_after"`
	ReservedDelta string    `json:"reserved_delta"`
	ReservedAfter string    `json:"reserved_after"`
	Reference     string    `json:"reference"`
	PaymentID     string    `json:"payment_id"`
	MessageID     string    `json:"message_id"`
	CreatedAt     time.Time `json:"created_at"`
}

LedgerEntry is one wallet ledger movement.

type LedgerParams

type LedgerParams struct {
	// Limit is 1..200; nil omits it. Use Int(n) to set it.
	Limit *int
	// Before returns entries with an id lower than this. 0 omits it.
	Before int64
}

LedgerParams pages Wallet.Ledger. It does not use cursors: pass the smallest ID already seen as Before, and stop when fewer than Limit entries come back.

type ListBatchItemsParams

type ListBatchItemsParams struct {
	Status string
	Limit  int
	Cursor string
}

ListBatchItemsParams filters Batches.ListItems.

func (ListBatchItemsParams) WithCursor

func (p ListBatchItemsParams) WithCursor(cursor string) ListBatchItemsParams

WithCursor implements Cursorable.

type ListFunc

type ListFunc[T any, P any] func(ctx context.Context, params P) (*Page[T], error)

ListFunc is the shape of every cursor list method.

type ListMessagesParams

type ListMessagesParams struct {
	// Limit is the page size (1..100, server default 20).
	Limit  int
	Cursor string
	// Status filters by message status.
	Status string
	// To filters by a digits or +digits fragment of the destination.
	To string
	// Country filters by uppercase ISO2.
	Country string
	// DateFrom and DateTo bound the creation time; zero omits them.
	DateFrom time.Time
	DateTo   time.Time
}

ListMessagesParams filters Messages.List.

func (ListMessagesParams) WithCursor

func (p ListMessagesParams) WithCursor(cursor string) ListMessagesParams

WithCursor implements Cursorable.

type ListParams

type ListParams struct {
	// Limit is the page size (1..200, server default 50). 0 omits it.
	Limit int
	// Cursor is a NextCursor from a previous page.
	Cursor string
}

ListParams are the cursor parameters shared by most list methods.

func (ListParams) WithCursor

func (p ListParams) WithCursor(cursor string) ListParams

WithCursor implements Cursorable.

type Lookup

type Lookup struct {
	ID        string     `json:"id"`
	State     string     `json:"state"`
	Country   string     `json:"country"`
	Carrier   *string    `json:"carrier"`
	Ported    *bool      `json:"ported"`
	Valid     *bool      `json:"valid"`
	Source    string     `json:"source"`
	Price     string     `json:"price"`
	Currency  string     `json:"currency"`
	CheckedAt *time.Time `json:"checked_at"`
}

Lookup is a number lookup.

type Lookups

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

Lookups is the number lookup resource, reached as client.Lookups.

func (*Lookups) Create

func (r *Lookups) Create(ctx context.Context, params CreateLookupParams, opts ...CallOption) (*Lookup, error)

Create runs a lookup (POST /v1/lookup). The result is completed (200) or still pending (202); poll Get for pending lookups.

func (*Lookups) Get

func (r *Lookups) Get(ctx context.Context, id string) (*Lookup, error)

Get fetches a lookup (GET /v1/lookup/{id}).

type Message

type Message struct {
	ID                string           `json:"id"`
	CreatedAt         time.Time        `json:"created_at"`
	To                string           `json:"to"`
	SenderID          string           `json:"sender_id"`
	Text              string           `json:"text"`
	Parts             int              `json:"parts"`
	Status            string           `json:"status"`
	StatusReason      string           `json:"status_reason"`
	SentAt            *time.Time       `json:"sent_at"`
	DeliveredAt       *time.Time       `json:"delivered_at"`
	FailedAt          *time.Time       `json:"failed_at"`
	CancelledAt       *time.Time       `json:"cancelled_at"`
	ScheduledAt       *time.Time       `json:"scheduled_at"`
	Price             string           `json:"price"`
	Currency          string           `json:"currency"`
	TrafficType       string           `json:"traffic_type"`
	Metadata          map[string]any   `json:"metadata"`
	Encoding          string           `json:"encoding"`
	CountryID         string           `json:"country_id"`
	CountryISO2       string           `json:"country_iso2"`
	CountryName       string           `json:"country_name"`
	CarrierID         string           `json:"carrier_id"`
	CarrierName       string           `json:"carrier_name"`
	DestinationSource string           `json:"destination_source"`
	Billing           []map[string]any `json:"billing"`
}

Message is an outbound SMS.

type Messages

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

Messages is the messages resource, reached as client.Messages.

func (*Messages) Attempts

func (r *Messages) Attempts(ctx context.Context, id string) ([]Attempt, error)

Attempts lists the provider submissions of a message (GET /v1/messages/{id}/attempts).

func (*Messages) Cancel

func (r *Messages) Cancel(ctx context.Context, id string) (*Message, error)

Cancel cancels a queued or scheduled message (POST /v1/messages/{id}/cancel). Any other state returns a 409 *Error. Never retried.

func (*Messages) Get

func (r *Messages) Get(ctx context.Context, id string) (*Message, error)

Get fetches one message (GET /v1/messages/{id}).

func (*Messages) List

func (r *Messages) List(ctx context.Context, params ListMessagesParams) (*Page[Message], error)

List returns a page of messages, newest first (GET /v1/messages).

func (*Messages) Send

func (r *Messages) Send(ctx context.Context, params SendMessageParams, opts ...CallOption) (*Message, error)

Send sends one SMS (POST /v1/messages). An Idempotency-Key is generated unless WithIdempotencyKey is passed, and reused on retries.

type Metrics

type Metrics struct {
	Sent         int64   `json:"sent"`
	Delivered    int64   `json:"delivered"`
	Failed       int64   `json:"failed"`
	Parts        int64   `json:"parts"`
	DeliveryRate float64 `json:"delivery_rate"`
	Spend        string  `json:"spend"`
	P50Ms        *int64  `json:"p50_ms"`
	P95Ms        *int64  `json:"p95_ms"`
}

Metrics are delivery and spend counters.

type Money

type Money struct {
	Amount   string `json:"amount"`
	Currency string `json:"currency"`
}

Money is an amount with its currency.

type Number

type Number struct {
	ID          string     `json:"id"`
	Country     string     `json:"country"`
	Number      string     `json:"number"`
	Kind        string     `json:"kind"`
	MonthlyFee  string     `json:"monthly_fee"`
	FeeCurrency string     `json:"fee_currency"`
	Status      string     `json:"status"`
	Inbound     bool       `json:"inbound"`
	Outbound    bool       `json:"outbound"`
	AssignedAt  *time.Time `json:"assigned_at"`
	RenewsAt    *time.Time `json:"renews_at"`
}

Number is a virtual number.

type NumberRule

type NumberRule struct {
	ID       string `json:"id"`
	Match    string `json:"match"`
	Pattern  string `json:"pattern"`
	Action   string `json:"action"`
	Target   string `json:"target"`
	Position int    `json:"position"`
}

NumberRule routes inbound messages on a number.

type NumberRuleParams

type NumberRuleParams struct {
	// Match is keyword, prefix, regex or any.
	Match string `json:"match"`
	// Pattern is required unless Match is any.
	Pattern string `json:"pattern,omitempty"`
	// Action is webhook, auto_reply or forward_email.
	Action   string `json:"action"`
	Target   string `json:"target"`
	Position int    `json:"position"`
}

NumberRuleParams is the body for Numbers.CreateRule and Numbers.UpdateRule.

type Numbers

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

Numbers manages virtual numbers, reached as client.Numbers. Everything except List and Available requires a live key.

func (*Numbers) Assign

func (r *Numbers) Assign(ctx context.Context, params AssignNumberParams, opts ...CallOption) (*Number, error)

Assign assigns a number and charges the wallet (POST /v1/numbers).

func (*Numbers) Available

func (r *Numbers) Available(ctx context.Context, params AvailableNumbersParams) ([]Number, error)

Available lists numbers that can be assigned (GET /v1/numbers/available).

func (*Numbers) CreateRule

func (r *Numbers) CreateRule(ctx context.Context, id string, params NumberRuleParams, opts ...CallOption) (*NumberRule, error)

CreateRule adds an inbound rule (POST /v1/numbers/{id}/rules).

func (*Numbers) DeleteRule

func (r *Numbers) DeleteRule(ctx context.Context, id, ruleID string) error

DeleteRule deletes an inbound rule (DELETE /v1/numbers/{id}/rules/{rule_id}).

func (*Numbers) List

func (r *Numbers) List(ctx context.Context, params ListParams) (*Page[Number], error)

List returns a page of assigned numbers (GET /v1/numbers).

func (*Numbers) ListRules

func (r *Numbers) ListRules(ctx context.Context, id string, params ListParams) (*Page[NumberRule], error)

ListRules returns a page of inbound rules (GET /v1/numbers/{id}/rules).

func (*Numbers) Release

func (r *Numbers) Release(ctx context.Context, id string) error

Release releases a number (DELETE /v1/numbers/{id}).

func (*Numbers) UpdateRule

func (r *Numbers) UpdateRule(ctx context.Context, id, ruleID string, params NumberRuleParams) (*NumberRule, error)

UpdateRule replaces an inbound rule (PUT /v1/numbers/{id}/rules/{rule_id}).

type OTP

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

OTP is the one-time passcode resource, reached as client.OTP.

func (*OTP) Send

func (r *OTP) Send(ctx context.Context, params SendOTPParams, opts ...CallOption) (*OTPSendResult, error)

Send generates and sends a code (POST /v1/otp/send).

func (*OTP) Verify

func (r *OTP) Verify(ctx context.Context, params VerifyOTPParams) (*OTPVerifyResult, error)

Verify checks a code (POST /v1/otp/verify). A wrong code returns Valid false and uses up an attempt, so Verify is never retried.

type OTPSendResult

type OTPSendResult struct {
	OTPID string `json:"otp_id"`
}

OTPSendResult is returned by OTP.Send.

type OTPVerifyResult

type OTPVerifyResult struct {
	Valid        bool `json:"valid"`
	AttemptsLeft int  `json:"attempts_left"`
}

OTPVerifyResult is returned by OTP.Verify.

type Option

type Option func(*transport)

Option configures a Client. Pass options to NewClient.

func WithBaseURL

func WithBaseURL(baseURL string) Option

WithBaseURL overrides the API base URL (default https://opensms.io). Trailing slashes are stripped.

func WithHTTPClient

func WithHTTPClient(hc *http.Client) Option

WithHTTPClient injects a custom *http.Client (for proxies or tests).

func WithMaxRetries

func WithMaxRetries(maxRetries int) Option

WithMaxRetries sets the number of retries after the first attempt (default 2, so 3 attempts in total). 0 disables retries.

func WithTimeout

func WithTimeout(timeout time.Duration) Option

WithTimeout sets the per-attempt timeout covering connect and read (default 30s). 0 disables the SDK timeout.

type Page

type Page[T any] struct {
	Items      []T    `json:"items"`
	NextCursor string `json:"next_cursor"`
}

Page is one page of a cursor-paginated list. NextCursor is empty on the last page; pass it back as Cursor to fetch the next one.

func (*Page[T]) HasMore

func (p *Page[T]) HasMore() bool

HasMore reports whether another page exists.

type PriceEntry

type PriceEntry struct {
	CountryISO2       string    `json:"country_iso2"`
	CountryName       string    `json:"country_name"`
	CarrierID         string    `json:"carrier_id"`
	CarrierName       string    `json:"carrier_name"`
	Product           string    `json:"product"`
	MinMonthlyVolume  int64     `json:"min_monthly_volume"`
	MarkupType        string    `json:"markup_type"`
	MarkupValue       string    `json:"markup_value"`
	SellCurrency      string    `json:"sell_currency"`
	SellAmount        *string   `json:"sell_amount"`
	ConvertedAmount   *string   `json:"converted_amount"`
	ConvertedCurrency string    `json:"converted_currency"`
	WorkspaceOverride bool      `json:"workspace_override"`
	EffectiveFrom     time.Time `json:"effective_from"`
	FXRate            string    `json:"fx_rate"`
}

PriceEntry is one row of a price list.

type PriceList

type PriceList struct {
	WorkspaceID string       `json:"workspace_id"`
	Currency    string       `json:"currency"`
	Product     string       `json:"product"`
	Entries     []PriceEntry `json:"entries"`
}

PriceList is returned by Pricing.Get.

type Pricing

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

Pricing reads the workspace price list, reached as client.Pricing.

func (*Pricing) Get

func (r *Pricing) Get(ctx context.Context, params PricingParams) (*PriceList, error)

Get returns the price list (GET /v1/pricing).

type PricingParams

type PricingParams struct {
	// Product is sms (default), lookup or number_monthly.
	Product string
	// Country is an ISO2 code.
	Country string
}

PricingParams filters Pricing.Get.

type QuietHours

type QuietHours struct {
	TrafficType string `json:"traffic_type"`
	StartLocal  string `json:"start_local"`
	EndLocal    string `json:"end_local"`
	Enforce     string `json:"enforce"`
}

QuietHours is a country quiet-hours window.

type QuoteSenderIDParams

type QuoteSenderIDParams struct {
	Countries []string
}

QuoteSenderIDParams is the query for SenderIDs.Quote.

type ReplayDeliveryParams

type ReplayDeliveryParams struct {
	// Generation comes from the delivery.
	Generation int `json:"generation"`
	// Reason is 5..1000 characters.
	Reason string `json:"reason"`
}

ReplayDeliveryParams is the body for Webhooks.ReplayDelivery.

type Route

type Route struct {
	Provider  string `json:"provider"`
	Carrier   string `json:"carrier"`
	Health    string `json:"health"`
	Cost      string `json:"cost"`
	Currency  string `json:"currency"`
	Priority  int    `json:"priority"`
	SellPrice *Money `json:"sell_price"`
	P50Ms     *int64 `json:"p50_ms"`
}

Route is a routable provider path for a country.

type Sandbox

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

Sandbox exposes sandbox-only helpers, reached as client.Sandbox.

func (*Sandbox) ListMessages

func (r *Sandbox) ListMessages(ctx context.Context, params ListParams) (*Page[SandboxMessage], error)

ListMessages returns a page of rendered sandbox sends, including OTP codes (GET /v1/sandbox/messages).

type SandboxMessage

type SandboxMessage struct {
	ID          string     `json:"id"`
	To          string     `json:"to"`
	SenderID    string     `json:"sender_id"`
	Text        string     `json:"text"`
	Parts       int        `json:"parts"`
	Status      string     `json:"status"`
	TrafficType string     `json:"traffic_type"`
	CreatedAt   time.Time  `json:"created_at"`
	SentAt      *time.Time `json:"sent_at"`
}

SandboxMessage is a rendered sandbox send (including OTP codes).

type SendMessageParams

type SendMessageParams struct {
	// To is the destination in E.164 (required).
	To string
	// Text is the message body, 1..1600 characters (required).
	Text string
	// SenderID is the sender (<=11 characters, or <=15 digits).
	SenderID string
	// TrafficType is otp, transactional (default) or marketing.
	TrafficType string
	// ScheduledAt schedules the send; zero sends now.
	ScheduledAt time.Time
	// CallbackURL receives delivery callbacks for this message.
	CallbackURL string
	// Metadata is a free-form JSON object stored with the message.
	Metadata map[string]any
}

SendMessageParams is the body for Messages.Send.

func (SendMessageParams) MarshalJSON

func (p SendMessageParams) MarshalJSON() ([]byte, error)

MarshalJSON emits only the fields that are set.

type SendOTPParams

type SendOTPParams struct {
	To       string `json:"to"`
	SenderID string `json:"sender_id,omitempty"`
	// Template must contain {{code}}.
	Template string `json:"template,omitempty"`
	// Length is the code length, 4..10 (default 6).
	Length int `json:"length,omitempty"`
	// TTLSeconds is 30..86400 (default 600).
	TTLSeconds int `json:"ttl_seconds,omitempty"`
}

SendOTPParams is the body for OTP.Send.

type SenderDocument

type SenderDocument struct {
	ID           string     `json:"id"`
	Kind         string     `json:"kind"`
	Filename     string     `json:"filename"`
	ContentType  string     `json:"content_type"`
	Size         int64      `json:"size"`
	ScanStatus   string     `json:"scan_status"`
	ReviewStatus string     `json:"review_status"`
	ReviewReason string     `json:"review_reason"`
	ReviewedAt   *time.Time `json:"reviewed_at"`
	Version      int        `json:"version"`
	SupersedesID string     `json:"supersedes_id"`
	IsCurrent    bool       `json:"is_current"`
	CreatedAt    time.Time  `json:"created_at"`
}

SenderDocument is an uploaded sender registration document.

type SenderID

type SenderID struct {
	ID                string           `json:"id"`
	Value             string           `json:"value"`
	Kind              string           `json:"kind"`
	Countries         []string         `json:"countries"`
	UseCase           string           `json:"use_case"`
	SampleMessage     string           `json:"sample_message"`
	Status            string           `json:"status"`
	RejectionReason   string           `json:"rejection_reason"`
	Restricted        bool             `json:"restricted"`
	RestrictionReason string           `json:"restriction_reason"`
	CreatedAt         time.Time        `json:"created_at"`
	Registrations     []map[string]any `json:"registrations"`
}

SenderID is a registered sender.

type SenderIDCheck

type SenderIDCheck struct {
	Valid     bool   `json:"valid"`
	Available bool   `json:"available"`
	Reserved  bool   `json:"reserved"`
	Reason    string `json:"reason"`
}

SenderIDCheck is returned by SenderIDs.Check.

type SenderIDDraft

type SenderIDDraft struct {
	ID                string    `json:"id"`
	Source            string    `json:"source"`
	Value             string    `json:"value"`
	Kind              string    `json:"kind"`
	Countries         []string  `json:"countries"`
	UseCase           string    `json:"use_case"`
	SampleMessage     string    `json:"sample_message"`
	Documents         []string  `json:"documents"`
	Version           int       `json:"version"`
	Status            string    `json:"status"`
	SubmittedSenderID *string   `json:"submitted_sender_id"`
	CreatedAt         time.Time `json:"created_at"`
	UpdatedAt         time.Time `json:"updated_at"`
}

SenderIDDraft is a saved, unsubmitted sender ID application.

type SenderIDDraftParams

type SenderIDDraftParams struct {
	// Source is onboarding or application.
	Source        string   `json:"source,omitempty"`
	Value         string   `json:"value,omitempty"`
	Kind          string   `json:"kind,omitempty"`
	Countries     []string `json:"countries,omitempty"`
	UseCase       string   `json:"use_case,omitempty"`
	SampleMessage string   `json:"sample_message,omitempty"`
	Documents     []string `json:"documents,omitempty"`
}

SenderIDDraftParams is the body for SenderIDs.CreateDraft.

type SenderIDQuote

type SenderIDQuote struct {
	QuoteID string               `json:"quote_id"`
	Entries []SenderIDQuoteEntry `json:"entries"`
	Totals  []Money              `json:"totals"`
}

SenderIDQuote is returned by SenderIDs.Quote.

type SenderIDQuoteEntry

type SenderIDQuoteEntry struct {
	Country     string `json:"country"`
	Provider    string `json:"provider"`
	FeeAmount   string `json:"fee_amount"`
	FeeCurrency string `json:"fee_currency"`
}

SenderIDQuoteEntry is one country fee in a quote.

type SenderIDs

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

SenderIDs manages sender IDs, drafts and documents, reached as client.SenderIDs. Document upload and download are console only.

func (*SenderIDs) Check

func (r *SenderIDs) Check(ctx context.Context, params CheckSenderIDParams) (*SenderIDCheck, error)

Check reports whether a value can be registered (GET /v1/sender-ids/check).

func (*SenderIDs) Create

func (r *SenderIDs) Create(ctx context.Context, params CreateSenderIDParams) (*SenderID, error)

Create submits a sender ID registration (POST /v1/sender-ids). It may charge fees (see Quote), so it is never retried.

func (*SenderIDs) CreateDraft

func (r *SenderIDs) CreateDraft(ctx context.Context, params SenderIDDraftParams) (*SenderIDDraft, error)

CreateDraft saves a draft application (POST /v1/sender-id-drafts). Never retried.

func (*SenderIDs) Delete

func (r *SenderIDs) Delete(ctx context.Context, id string) error

Delete deletes a sender ID (DELETE /v1/sender-ids/{id}).

func (*SenderIDs) DeleteDraft

func (r *SenderIDs) DeleteDraft(ctx context.Context, id string) error

DeleteDraft deletes a draft (DELETE /v1/sender-id-drafts/{id}).

func (*SenderIDs) Get

func (r *SenderIDs) Get(ctx context.Context, id string) (*SenderID, error)

Get fetches a sender ID with its registrations (GET /v1/sender-ids/{id}).

func (*SenderIDs) GetDraft

func (r *SenderIDs) GetDraft(ctx context.Context, id string) (*SenderIDDraft, error)

GetDraft fetches a draft (GET /v1/sender-id-drafts/{id}).

func (*SenderIDs) List

func (r *SenderIDs) List(ctx context.Context, params ListParams) (*Page[SenderID], error)

List returns a page of sender IDs (GET /v1/sender-ids).

func (*SenderIDs) ListDocuments

func (r *SenderIDs) ListDocuments(ctx context.Context) ([]SenderDocument, error)

ListDocuments lists uploaded sender documents (GET /v1/sender-documents).

func (*SenderIDs) ListDrafts

func (r *SenderIDs) ListDrafts(ctx context.Context, params ListParams) (*Page[SenderIDDraft], error)

ListDrafts returns a page of drafts (GET /v1/sender-id-drafts).

func (*SenderIDs) Quote

func (r *SenderIDs) Quote(ctx context.Context, params QuoteSenderIDParams) (*SenderIDQuote, error)

Quote prices registration in the given countries (GET /v1/sender-ids/quote?countries=KE,NG).

func (*SenderIDs) Update

func (r *SenderIDs) Update(ctx context.Context, id string, params UpdateSenderIDParams) (*SenderID, error)

Update amends a sender ID registration (PATCH /v1/sender-ids/{id}).

func (*SenderIDs) UpdateDraft

func (r *SenderIDs) UpdateDraft(ctx context.Context, id string, params UpdateSenderIDDraftParams) (*SenderIDDraft, error)

UpdateDraft changes a draft; params.Version must be the current version (PATCH /v1/sender-id-drafts/{id}).

type StatusResult

type StatusResult struct {
	Status string `json:"status"`
}

StatusResult is a {status} acknowledgement (webhook test and replay).

type Suppression

type Suppression struct {
	ID        int64     `json:"id"`
	E164      string    `json:"e164"`
	Reason    string    `json:"reason"`
	CreatedAt time.Time `json:"created_at"`
}

Suppression is a do-not-send entry.

type SuppressionImportResult

type SuppressionImportResult struct {
	Created  int `json:"created"`
	Received int `json:"received"`
}

SuppressionImportResult is returned by Suppressions.Import.

type SuppressionInput

type SuppressionInput struct {
	E164 string `json:"e164"`
	// Reason is stop_keyword, manual, complaint or invalid_number.
	Reason string `json:"reason"`
}

SuppressionInput is the body for Suppressions.Create and one import item.

type Suppressions

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

Suppressions manages the do-not-send list, reached as client.Suppressions.

func (*Suppressions) Create

Create suppresses a number (POST /v1/compliance/suppressions). Never retried.

func (*Suppressions) Delete

func (r *Suppressions) Delete(ctx context.Context, id int64) error

Delete removes a suppression (DELETE /v1/compliance/suppressions/{id}).

func (*Suppressions) Import

Import suppresses many numbers (POST /v1/compliance/suppressions/import). Never retried.

func (*Suppressions) List

func (r *Suppressions) List(ctx context.Context, params ListParams) (*Page[Suppression], error)

List returns a page of suppressions (GET /v1/compliance/suppressions).

type Template

type Template struct {
	ID          string    `json:"id"`
	WorkspaceID string    `json:"workspace_id"`
	Name        string    `json:"name"`
	Body        string    `json:"body"`
	TrafficType string    `json:"traffic_type"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	Variables   []string  `json:"variables"`
}

Template is a reusable message body with {{name}} placeholders.

type Templates

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

Templates manages message templates, reached as client.Templates.

func (*Templates) Create

func (r *Templates) Create(ctx context.Context, params CreateTemplateParams, opts ...CallOption) (*Template, error)

Create creates a template (POST /v1/templates). Variables are parsed from {{name}} placeholders by the server.

func (*Templates) Delete

func (r *Templates) Delete(ctx context.Context, id string) error

Delete deletes a template (DELETE /v1/templates/{id}).

func (*Templates) Get

func (r *Templates) Get(ctx context.Context, id string) (*Template, error)

Get fetches a template (GET /v1/templates/{id}).

func (*Templates) List

func (r *Templates) List(ctx context.Context, params ListParams) (*Page[Template], error)

List returns a page of templates (GET /v1/templates).

func (*Templates) Update

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

Update changes the given fields of a template (PATCH /v1/templates/{id}).

type Topup

type Topup struct {
	ID               string `json:"id"`
	Reference        string `json:"reference"`
	AuthorizationURL string `json:"authorization_url"`
	AccessCode       string `json:"access_code"`
	Amount           string `json:"amount"`
	Currency         string `json:"currency"`
	Status           string `json:"status"`
}

Topup is a pending payment-provider top-up.

type UpdateContactGroupParams

type UpdateContactGroupParams struct {
	Name       string
	ContactIDs []string
}

UpdateContactGroupParams is the body for ContactGroups.Update. A nil ContactIDs keeps the members; a non-nil empty slice clears them.

func (UpdateContactGroupParams) MarshalJSON

func (p UpdateContactGroupParams) MarshalJSON() ([]byte, error)

MarshalJSON sends contact_ids whenever ContactIDs is non-nil.

type UpdateContactParams

type UpdateContactParams struct {
	E164       string         `json:"e164,omitempty"`
	Name       string         `json:"name,omitempty"`
	Attributes map[string]any `json:"attributes,omitempty"`
}

UpdateContactParams is the body for Contacts.Update. Unset fields are kept.

type UpdateSenderIDDraftParams

type UpdateSenderIDDraftParams struct {
	Version       int      `json:"version"`
	Value         string   `json:"value,omitempty"`
	Kind          string   `json:"kind,omitempty"`
	Countries     []string `json:"countries,omitempty"`
	UseCase       string   `json:"use_case,omitempty"`
	SampleMessage string   `json:"sample_message,omitempty"`
	Documents     []string `json:"documents,omitempty"`
}

UpdateSenderIDDraftParams is the body for SenderIDs.UpdateDraft. Version is the draft's current version (optimistic lock, 409 on mismatch).

type UpdateSenderIDParams

type UpdateSenderIDParams struct {
	UseCase       string   `json:"use_case"`
	Countries     []string `json:"countries"`
	Documents     []string `json:"documents"`
	SampleMessage string   `json:"sample_message,omitempty"`
}

UpdateSenderIDParams is the body for SenderIDs.Update.

type UpdateTemplateParams

type UpdateTemplateParams struct {
	Name        string `json:"name,omitempty"`
	Body        string `json:"body,omitempty"`
	TrafficType string `json:"traffic_type,omitempty"`
}

UpdateTemplateParams is the body for Templates.Update. Unset fields are kept.

type UpdateWebhookParams

type UpdateWebhookParams struct {
	URL     string   `json:"url"`
	Events  []string `json:"events"`
	Enabled bool     `json:"enabled"`
}

UpdateWebhookParams is the body for Webhooks.Update, a full replacement: every field is sent.

type ValidationReport

type ValidationReport struct {
	Rows       []ValidationRow `json:"rows"`
	Total      int             `json:"total"`
	Valid      int             `json:"valid"`
	Invalid    int             `json:"invalid"`
	Duplicates int             `json:"duplicates"`
	Suppressed int             `json:"suppressed"`
}

ValidationReport is returned by Batches.Validation.

type ValidationRow

type ValidationRow struct {
	Row        int            `json:"row"`
	Item       BatchItemInput `json:"item"`
	Valid      bool           `json:"valid"`
	Duplicate  bool           `json:"duplicate"`
	Suppressed bool           `json:"suppressed"`
	Error      string         `json:"error"`
}

ValidationRow is one row of a batch validation report.

type VerifyOTPParams

type VerifyOTPParams struct {
	OTPID string `json:"otp_id"`
	Code  string `json:"code"`
}

VerifyOTPParams is the body for OTP.Verify.

type VerifyOptions

type VerifyOptions struct {
	// Tolerance is the allowed difference between now and the signed
	// timestamp (default 300s). The boundary is inclusive.
	Tolerance time.Duration
	// Now overrides the current time (for tests).
	Now time.Time
}

VerifyOptions tunes signature verification.

type Wallet

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

Wallet reads balances and the ledger, reached as client.Wallet.

func (*Wallet) Balances

func (r *Wallet) Balances(ctx context.Context) ([]WalletBalance, error)

Balances returns every currency wallet (GET /v1/wallet).

func (*Wallet) CreateTopup

func (r *Wallet) CreateTopup(ctx context.Context, params CreateTopupParams, opts ...CallOption) (*Topup, error)

CreateTopup starts a payment-provider top-up (POST /v1/wallet/topups). Live keys only.

func (*Wallet) Ledger

func (r *Wallet) Ledger(ctx context.Context, params LedgerParams) ([]LedgerEntry, error)

Ledger returns ledger entries, newest first (GET /v1/wallet/ledger). It pages with Before (the smallest id seen), not cursors.

type WalletBalance

type WalletBalance struct {
	ID          string `json:"id"`
	Currency    string `json:"currency"`
	Balance     string `json:"balance"`
	Reserved    string `json:"reserved"`
	Environment string `json:"environment"`
}

WalletBalance is one currency wallet.

type WebhookDelivery

type WebhookDelivery struct {
	ID               int64      `json:"id"`
	Generation       int        `json:"generation"`
	Event            string     `json:"event"`
	Payload          any        `json:"payload"`
	Attempts         int        `json:"attempts"`
	NextRetryAt      *time.Time `json:"next_retry_at"`
	Status           string     `json:"status"`
	LastResponseCode *int       `json:"last_response_code"`
	LastError        string     `json:"last_error"`
	CreatedAt        time.Time  `json:"created_at"`
	DeliveredAt      *time.Time `json:"delivered_at"`
}

WebhookDelivery is one delivery of an event to an endpoint.

type WebhookEndpoint

type WebhookEndpoint struct {
	ID                  string     `json:"id"`
	URL                 string     `json:"url"`
	Events              []string   `json:"events"`
	Enabled             bool       `json:"enabled"`
	ConsecutiveFailures int        `json:"consecutive_failures"`
	DisabledAt          *time.Time `json:"disabled_at"`
	CreatedAt           time.Time  `json:"created_at"`
	// Secret (whsec_...) is returned only by Webhooks.Create. Store it.
	Secret string `json:"secret"`
}

WebhookEndpoint is a webhook subscription.

type WebhookEvent

type WebhookEvent struct {
	ID          string         `json:"id"`
	Type        string         `json:"type"`
	WorkspaceID string         `json:"workspace_id"`
	Environment string         `json:"environment"`
	CreatedAt   time.Time      `json:"created_at"`
	Data        map[string]any `json:"data"`
}

WebhookEvent is the envelope of a signed webhook delivery.

func ConstructEvent

func ConstructEvent(payload []byte, header, secret string, opts ...VerifyOptions) (*WebhookEvent, error)

ConstructEvent verifies the signature and then decodes the event envelope. A bad signature returns *Error with Status 0 and Code "invalid_signature"; a timestamp outside the tolerance returns Code "expired_signature".

type Webhooks

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

Webhooks manages webhook endpoints, reached as client.Webhooks. Signature verification is available here and as the package functions VerifySignature and ConstructEvent.

func (*Webhooks) ConstructEvent

func (r *Webhooks) ConstructEvent(payload []byte, header, secret string, opts ...VerifyOptions) (*WebhookEvent, error)

ConstructEvent is ConstructEvent, reachable from the client.

func (*Webhooks) Create

func (r *Webhooks) Create(ctx context.Context, params CreateWebhookParams, opts ...CallOption) (*WebhookEndpoint, error)

Create creates an endpoint (POST /v1/webhooks). The returned Secret (whsec_...) is shown only once.

func (*Webhooks) Delete

func (r *Webhooks) Delete(ctx context.Context, id string, opts ...CallOption) error

Delete deletes an endpoint (DELETE /v1/webhooks/{id}).

func (*Webhooks) Get

func (r *Webhooks) Get(ctx context.Context, id string) (*WebhookEndpoint, error)

Get fetches an endpoint (GET /v1/webhooks/{id}).

func (*Webhooks) List

func (r *Webhooks) List(ctx context.Context, params ListParams) (*Page[WebhookEndpoint], error)

List returns a page of endpoints (GET /v1/webhooks).

func (*Webhooks) ListDeliveries

func (r *Webhooks) ListDeliveries(ctx context.Context, id string, params ListParams) (*Page[WebhookDelivery], error)

ListDeliveries returns a page of deliveries for an endpoint (GET /v1/webhooks/{id}/deliveries).

func (*Webhooks) ReplayDelivery

func (r *Webhooks) ReplayDelivery(ctx context.Context, id string, deliveryID int64, params ReplayDeliveryParams, opts ...CallOption) (*StatusResult, error)

ReplayDelivery re-queues a delivery (POST /v1/webhooks/{id}/deliveries/{delivery_id}/replay).

func (*Webhooks) Test

func (r *Webhooks) Test(ctx context.Context, id string, opts ...CallOption) (*StatusResult, error)

Test queues a webhook.test delivery (POST /v1/webhooks/{id}/test).

func (*Webhooks) Update

func (r *Webhooks) Update(ctx context.Context, id string, params UpdateWebhookParams, opts ...CallOption) (*WebhookEndpoint, error)

Update replaces an endpoint (PUT /v1/webhooks/{id}). URL, Events and Enabled are all sent.

func (*Webhooks) VerifySignature

func (r *Webhooks) VerifySignature(payload []byte, header, secret string, opts ...VerifyOptions) bool

VerifySignature is VerifySignature, reachable from the client.

Jump to

Keyboard shortcuts

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