esms

package module
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Jul 29, 2026 License: MIT Imports: 12 Imported by: 0

README

esms-go

Official Go SDK for the eSMS Africa SMS API.

Send SMS across 14+ African countries, track delivery, schedule messages, and check your balance. Standard library only — no third-party dependencies.

Install

go get github.com/eSMS-Africa/esms-go

Quick start

package main

import (
	"context"
	"fmt"
	"log"
	"os"

	esms "github.com/eSMS-Africa/esms-go"
)

func main() {
	client := esms.New(os.Getenv("ESMS_API_KEY"))

	res, err := client.Messages.Send(context.Background(), esms.SendParams{
		To:       "+256700000000",
		Text:     "Your verification code is 123456",
		SenderID: "eSMSAfrica", // optional — falls back to the route default
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(res.ID, res.Status) // "...", "submitted"
}

Get an API key from the eSMS dashboard under Developers → API Keys. Live keys look like esms_live_…; test keys look like esms_test_….

Sending

ctx := context.Background()

// Auto-detects the route (country) from the number.
client.Messages.Send(ctx, esms.SendParams{To: "+254711000000", Text: "Hi from Kenya"})

// Or pin a route explicitly.
client.Messages.Send(ctx, esms.SendParams{To: "+256700000000", Text: "Hi", Route: "ESMS_UG"})

// Schedule for later (5 minutes to 7 days out).
client.Messages.Schedule(ctx, esms.SendParams{
	To:          "+256700000000",
	Text:        "Reminder",
	ScheduledAt: "2026-08-01T09:00:00Z",
})

Delivery status

msg, _ := client.Messages.Get(ctx, res.ID)
fmt.Println(msg.Status) // queued | submitted | delivered | failed | ...
for _, e := range msg.Timeline {
	fmt.Println(e.At, e.Event)
}

// List recent messages
page, _ := client.Messages.List(ctx, esms.ListParams{Limit: 20, Status: "delivered"})
fmt.Println(page.Total)

// Retry a failed one
client.Messages.Retry(ctx, res.ID)

Balance & routes

bal, _ := client.Balance.Get(ctx)
fmt.Printf("%s %.2f\n", bal.Currency, bal.Balance)

routes, _ := client.Routes.List(ctx)
for _, r := range routes {
	fmt.Println(r.Code, r.CountryName, r.Currency, r.PricePerSegment)
}

Errors

Every API failure is an *esms.Error. Use errors.As and the helper methods:

import "errors"

res, err := client.Messages.Send(ctx, params)
if err != nil {
	var e *esms.Error
	if errors.As(err, &e) {
		switch {
		case e.IsInsufficientBalance():
			bal, _ := e.Balance()
			cost, _ := e.Cost()
			cur, _ := e.Currency()
			log.Printf("top up needed: have %.2f, need %.2f %s", bal, cost, cur)
		case e.IsAuthentication():
			log.Print("check your API key")
		default:
			log.Printf("%d %s: %s", e.Status, e.Code, e.Message)
		}
	}
}
Helper When
IsAuthentication() 401 — key missing or invalid
IsPermission() 403 — not allowed
IsNotFound() 404 — no such message
IsInsufficientBalance() 422 — not enough credit (Balance(), Cost(), Currency())
IsRateLimit() 429 — slow down
IsConnection() network failure or timeout

Configuration

client := esms.New("esms_live_...",
	esms.WithBaseURL("https://sms.esmsafrica.io/api"), // default
	esms.WithMaxRetries(2),                            // transient failures with backoff
	esms.WithHTTPClient(&http.Client{Timeout: 30 * time.Second}),
)

Requests take a context.Context, so timeouts and cancellation work as usual.

License

MIT © eSMS Africa

Documentation

Overview

Package esms is the official Go SDK for the eSMS Africa SMS API.

Send SMS across African countries, track delivery, schedule messages, and check your balance.

client := esms.New("esms_live_...")
res, err := client.Messages.Send(ctx, esms.SendParams{
    To:   "+256700000000",
    Text: "Hello!",
})

Index

Constants

View Source
const (
	// DefaultBaseURL is the production API endpoint.
	DefaultBaseURL = "https://sms.esmsafrica.io/api"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type Balance

type Balance struct {
	Balance     float64 `json:"balance"`
	Currency    string  `json:"currency"`
	SMSEstimate *int    `json:"sms_estimate"`
}

Balance is the current account balance.

type BalanceService

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

BalanceService reads the account balance.

func (*BalanceService) Get

func (s *BalanceService) Get(ctx context.Context) (*Balance, error)

Get returns the current account balance and an SMS estimate.

type BulkSendParams

type BulkSendParams struct {
	ContactListIDs []int  `json:"contact_list_ids"`
	Text           string `json:"text"`
	SenderID       string `json:"sender_id,omitempty"`
	Route          string `json:"route,omitempty"`
}

BulkSendParams sends one message to every contact in the given lists.

type Client

type Client struct {

	// Messages sends and manages SMS messages.
	Messages *MessagesService
	// Balance reads the account balance.
	Balance *BalanceService
	// Routes lists available routes and pricing.
	Routes *RoutesService
	// contains filtered or unexported fields
}

Client is the eSMS Africa API client. Create one with New.

func New

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

New creates a Client. The apiKey is your esms_live_... or esms_test_... key.

func (*Client) BaseURL

func (c *Client) BaseURL() string

BaseURL returns the URL requests are sent to.

type Error

type Error struct {
	// Status is the HTTP status code (0 for connection failures).
	Status int
	// Code is the machine-readable error code, e.g. "insufficient_balance".
	Code string
	// Message is a human-readable description.
	Message string
	// Detail is the raw `detail` payload returned by the API.
	Detail any
	// RequestID is the X-Request-Id response header, useful for support.
	RequestID string
	// Err is the underlying cause for connection failures.
	Err error
}

Error is returned for every failure that originates from the eSMS API. Inspect Status and Code to branch, or use errors.As with the typed helpers.

func (*Error) Balance

func (e *Error) Balance() (float64, bool)

Balance returns the current balance from an insufficient_balance error.

func (*Error) Cost

func (e *Error) Cost() (float64, bool)

Cost returns the required cost from an insufficient_balance error.

func (*Error) Currency

func (e *Error) Currency() (string, bool)

Currency returns the currency from an insufficient_balance error.

func (*Error) Error

func (e *Error) Error() string

func (*Error) IsAuthentication

func (e *Error) IsAuthentication() bool

IsAuthentication reports a 401 (missing or invalid API key).

func (*Error) IsConnection

func (e *Error) IsConnection() bool

IsConnection reports a network/transport failure (never reached the API).

func (*Error) IsInsufficientBalance

func (e *Error) IsInsufficientBalance() bool

IsInsufficientBalance reports the 422 insufficient_balance error. When true, use Balance/Cost/Currency to read the shortfall.

func (*Error) IsNotFound

func (e *Error) IsNotFound() bool

IsNotFound reports a 404.

func (*Error) IsPermission

func (e *Error) IsPermission() bool

IsPermission reports a 403 (authenticated but not allowed).

func (*Error) IsRateLimit

func (e *Error) IsRateLimit() bool

IsRateLimit reports a 429.

func (*Error) Unwrap

func (e *Error) Unwrap() error

type ListParams

type ListParams struct {
	Page   int    // zero-based page index
	Limit  int    // page size, 1-100
	Status string // optional status filter, e.g. "delivered"
}

ListParams filters a message listing.

type Message

type Message struct {
	MessageSummary
	ErrorMessage *string         `json:"error_message"`
	SubmittedAt  *string         `json:"submitted_at"`
	FailedAt     *string         `json:"failed_at"`
	Timeline     []TimelineEvent `json:"timeline"`
}

Message is a single message with its full delivery timeline.

type MessageList

type MessageList struct {
	Messages []MessageSummary `json:"messages"`
	Total    int              `json:"total"`
	Page     int              `json:"page"`
	Limit    int              `json:"limit"`
}

MessageList is a page of messages.

type MessageSummary

type MessageSummary struct {
	ID          string  `json:"id"`
	Phone       string  `json:"phone"`
	Text        string  `json:"text"`
	SenderID    *string `json:"sender_id"`
	Route       *string `json:"route"`
	Country     *string `json:"country"`
	Segments    int     `json:"segments"`
	Cost        float64 `json:"cost"`
	Currency    *string `json:"currency"`
	Status      string  `json:"status"`
	ErrorCode   *string `json:"error_code"`
	RetryCount  int     `json:"retry_count"`
	CreatedAt   string  `json:"created_at"`
	DeliveredAt *string `json:"delivered_at"`
}

MessageSummary is a single row in a message listing.

type MessagesService

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

MessagesService handles SMS message operations.

func (*MessagesService) Get

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

Get fetches a single message with its full delivery timeline.

func (*MessagesService) List

func (s *MessagesService) List(ctx context.Context, params ListParams) (*MessageList, error)

List returns messages, most recent first.

func (*MessagesService) Retry

func (s *MessagesService) Retry(ctx context.Context, messageID string) (*SendResult, error)

Retry retries a failed message.

func (*MessagesService) Schedule

func (s *MessagesService) Schedule(ctx context.Context, params SendParams) (*SendResult, error)

Schedule sends an SMS for later delivery (5 minutes to 7 days out). The ScheduleMode field is set automatically.

func (*MessagesService) Send

func (s *MessagesService) Send(ctx context.Context, params SendParams) (*SendResult, error)

Send sends a single SMS.

func (*MessagesService) SendBulk

func (s *MessagesService) SendBulk(ctx context.Context, params BulkSendParams) (map[string]any, error)

SendBulk sends one message to every contact in the given contact lists.

type Option

type Option func(*Client)

Option customises a Client.

func WithBaseURL

func WithBaseURL(u string) Option

WithBaseURL overrides the API base URL.

func WithHTTPClient

func WithHTTPClient(h *http.Client) Option

WithHTTPClient supplies a custom *http.Client (timeouts, proxies, transport).

func WithMaxRetries

func WithMaxRetries(n int) Option

WithMaxRetries sets how many times transient failures (network, 429, 5xx) are retried with backoff. Default 2.

type Route

type Route struct {
	Code            string  `json:"code"`
	Name            string  `json:"name"`
	CountryCode     string  `json:"country_code"`
	CountryName     string  `json:"country_name"`
	Currency        string  `json:"currency"`
	PricePerSegment float64 `json:"price_per_segment"`
	SenderIDDefault string  `json:"sender_id_default"`
	IsActive        bool    `json:"is_active"`
}

Route is an active SMS route (one per reachable country).

type RoutesService

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

RoutesService lists available routes.

func (*RoutesService) List

func (s *RoutesService) List(ctx context.Context) ([]Route, error)

List returns all active routes (one per reachable country).

type SendParams

type SendParams struct {
	// To is the recipient in international format, e.g. "+256700000000".
	To string `json:"to"`
	// Text is the message body (Unicode supported).
	Text string `json:"text"`
	// SenderID is an approved sender ID; defaults to the route's default.
	SenderID string `json:"sender_id,omitempty"`
	// Route pins an explicit route code such as "ESMS_UG".
	Route string `json:"route,omitempty"`
	// ScheduleMode is "now" (default) or "scheduled".
	ScheduleMode string `json:"schedule_mode,omitempty"`
	// ScheduledAt is an ISO-8601 UTC time; required when ScheduleMode is "scheduled".
	ScheduledAt string `json:"scheduled_at,omitempty"`
}

SendParams describes a single SMS to send.

type SendResult

type SendResult struct {
	ID           string  `json:"id"`
	Status       string  `json:"status"`
	Segments     int     `json:"segments"`
	Cost         float64 `json:"cost"`
	CostCurrency string  `json:"cost_currency"`
	RouteCost    float64 `json:"route_cost"`
	RouteCurr    string  `json:"route_currency"`
	Route        string  `json:"route"`
	BalanceAfter float64 `json:"balance_after"`
	ScheduledAt  *string `json:"scheduled_at"`
}

SendResult is returned when a message is accepted.

type TimelineEvent

type TimelineEvent struct {
	Event    string         `json:"event"`
	Status   string         `json:"status"`
	Detail   *string        `json:"detail"`
	At       string         `json:"at"`
	Metadata map[string]any `json:"metadata"`
}

TimelineEvent is one entry in a message's delivery history.

Jump to

Keyboard shortcuts

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