SendAfrica CLI
The official command-line client for the API. Use it for smoke tests, account inspection, automation, and scripting SMS workflows from a terminal or a CI job. It covers all non-admin developer operations — admin routes are intentionally excluded. Command parsing is done with Cobra and configuration binding with Viper .
Install
Requires Go 1.25 or newer:
go install github.com/cameltech/sendafrica-cli/cmd/sendafrica@latestVerify the install:
sendafrica --help
sendafrica healthSecurity model
Credentials are never loaded from .env files. They come from exactly three places, in priority order (highest wins):
- CLI flags —
--api-key,--token,--api-url - Environment variables —
SENDAFRICA_API_KEY,SENDAFRICA_JWT_TOKEN,SENDAFRICA_API_URL,SENDAFRICA_OUTPUT,SENDAFRICA_PROFILE - Config profile — the currently active profile in
~/.config/sendafrica-cli/config.json
Other guarantees:
- The config file is written with
0600permissions and its directory with0700. sendafrica config showmasks secrets (keeps only the first 4 and last 4 characters).- When both an API key and a JWT are set, the API key wins. JWT is used only when no API key is present.
- Profile credentials are read from the config file, never passed as visible command-line flags.
POST /v1/sms/requires an API key (a JWT alone will be rejected there);sms-bulkaccepts both.
Configure credentials
Manual per-command (good for one-offs in CI):
sendafrica sms-send \
--api-key SA-your-key \
--api-url https://api.sendafrica.online \
--to 0712345678 --message "Hello from the CLI"Via environment (recommended for shells, CI, and agents):
export SENDAFRICA_API_KEY="SA-your-key"
export SENDAFRICA_API_URL="https://api.sendafrica.online"Via a named profile (recommended for local multi-account work):
sendafrica config add-profile work --api-key SA-your-key
sendafrica config use work
sendafrica config list
sendafrica config show # resolved config, secrets maskedQuick start
# Health and public reads (no credentials needed)
sendafrica health
sendafrica rates --country TZ
# Sending and reads
sendafrica sms-send --to 0712345678 --message "Your verification code is 4921." --output json
sendafrica credits-balance --output json
sendafrica sms-logs --output json --page 1 --per-page 100
# A full workflow: register, verify, create a key
sendafrica register --email dev@example.com --password your-password --first-name Dev --last-name Example
sendafrica verify-email --email dev@example.com --otp 123456
sendafrica login --email dev@example.com --password your-password --save
sendafrica api-keys create "ci-runner"Output formats
Every command accepts --output / -o:
table(default) — human-readable tables and key/value pairsjson— structured output for scripts and agentsyaml— YAML output
sendafrica credits-balance -o json
sendafrica sms-logs -o json --page 1 --per-page 100
sendafrica vouchers rate -o jsonCommand reference
Public (no auth)
| Command | Endpoint |
|---|---|
sendafrica health | GET /health |
sendafrica packages | GET /v1/packages |
sendafrica templates | GET /v1/templates |
sendafrica rates | GET /v1/rates |
sendafrica rates --country TZ | GET /v1/rates/TZ |
Authentication
| Command | Endpoint | Auth |
|---|---|---|
sendafrica register --email ... --password ... --first-name ... --last-name ... | POST /v1/auth/register | None |
sendafrica login --email ... --password ... | POST /v1/auth/login | None |
sendafrica refresh | POST /v1/auth/refresh | Refresh token |
sendafrica logout | POST /v1/auth/logout | JWT |
sendafrica verify-email --email ... --otp ... | POST /v1/auth/verify-email | None |
sendafrica send-verification-email --email ... | POST /v1/auth/send-verification-email | None |
sendafrica reset-password --email ... | POST /v1/auth/reset-password | None |
sendafrica reset-password-confirm --email ... --otp ... --password ... | POST /v1/auth/reset-password-confirm | None |
sendafrica me | GET /v1/auth/me | JWT/API key |
sendafrica me --update --name ... | PUT /v1/auth/me | JWT/API key |
sendafrica change-password --current-password ... --new-password ... | POST /v1/auth/change-password | JWT |
sendafrica verify-phone --phone ... --otp ... | POST /v1/auth/verify-phone | JWT |
sendafrica send-phone-otp --phone ... | POST /v1/auth/send-phone-otp | JWT |
Use sendafrica login --save to persist the JWT to the current profile, and --print-token to emit the raw token for piping into SENDAFRICA_JWT_TOKEN in scripts.
API keys (JWT only)
| Command | Endpoint |
|---|---|
sendafrica api-keys list | GET /v1/auth/api-keys |
sendafrica api-keys create [name] | POST /v1/auth/api-keys |
sendafrica api-keys get [keyId] | GET /v1/auth/api-keys/{keyId} |
sendafrica api-keys delete [keyId] | DELETE /v1/auth/api-keys/{keyId} |
SMS
| Command | Endpoint | Auth |
|---|---|---|
sendafrica sms-send --to ... --message ... | POST /v1/sms/ | API key |
sendafrica sms-bulk --to ... --message ... | POST /v1/sms/bulk | JWT/API key |
sendafrica sms-logs | GET /v1/sms/logs | JWT/API key |
POST /v1/sms/ requires an API key; use sms-bulk if only a JWT is available.
Credits
| Command | Endpoint |
|---|---|
sendafrica credits-balance | GET /v1/credits/balance |
sendafrica credits-history | GET /v1/credits/history |
Contacts
| Command | Endpoint |
|---|---|
sendafrica contacts list | GET /v1/contact-lists |
sendafrica contacts create [name] | POST /v1/contact-lists |
sendafrica contacts update [listId] --name ... | PUT /v1/contact-lists/{listId} |
sendafrica contacts delete [listId] | DELETE /v1/contact-lists/{listId} |
sendafrica contacts duplicate-check [listId] | GET /v1/contact-lists/{listId}/duplicate-check |
sendafrica contacts import [listId] [file.csv] | POST /v1/contact-lists/{listId}/import |
sendafrica contacts list-contacts [listId] | GET /v1/contact-lists/{listId}/contacts |
sendafrica contacts add-contact [listId] [phones] | POST /v1/contact-lists/{listId}/contacts |
sendafrica contacts get-contact [listId] [contactId] | GET /v1/contact-lists/{listId}/contacts/{contactId} |
sendafrica contacts update-contact [listId] [contactId] | PUT /v1/contact-lists/{listId}/contacts/{contactId} |
sendafrica contacts delete-contact [listId] [contactId] | DELETE /v1/contact-lists/{listId}/contacts/{contactId} |
sendafrica contacts add-phone [listId] [contactId] [phone] | POST /v1/contact-lists/{listId}/contacts/{contactId}/phones |
sendafrica contacts delete-phone [listId] [contactId] [phoneId] | DELETE /v1/contact-lists/{listId}/contacts/{contactId}/phones/{phoneId} |
sendafrica contacts export [listId] | GET /v1/contact-lists/{listId}/contacts/export |
sendafrica contacts google-status | GET /v1/contact-lists/google/status |
sendafrica contacts google-sync | POST /v1/contact-lists/google/sync |
sendafrica contacts google-disconnect | DELETE /v1/contact-lists/google/disconnect |
Campaigns
| Command | Endpoint |
|---|---|
sendafrica campaigns list | GET /v1/campaigns |
sendafrica campaigns create --name ... --message ... | POST /v1/campaigns |
sendafrica campaigns get [id] | GET /v1/campaigns/{id} |
sendafrica campaigns update [id] --name ... --message ... | PATCH /v1/campaigns/{id} |
sendafrica campaigns cancel [id] | POST /v1/campaigns/{id}/cancel |
sendafrica campaigns delete [id] | DELETE /v1/campaigns/{id} |
sendafrica campaigns schedule [id] [listId] | POST /v1/campaigns/{id}/schedule |
sendafrica campaigns recipients [id] | GET /v1/campaigns/{id}/recipients |
Vouchers (credits top-up)
| Command | Endpoint |
|---|---|
sendafrica vouchers rate | GET /v1/vouchers/rate |
sendafrica vouchers purchase [amount] | POST /v1/vouchers/ |
sendafrica vouchers send-otp [phone] | POST /v1/vouchers/otp/send |
sendafrica vouchers verify-otp [phone] [otp] | POST /v1/vouchers/otp/verify |
Sender IDs
Inspect requirements before submitting, then create the request. The sample message must be a real example between 50 and 500 characters, and the sender ID must only use the allowed characters shown by sender-ids requirements.
sendafrica sender-ids requirements
sendafrica sender-ids create \
--name MYBRAND \
--country TZ \
--purpose Transactional \
--sample-message "Your verification code is 123456. It expires in ten minutes." \
--documents-file documents.jsondocuments.json is a JSON array indexed by the requirement UIDs returned by sender-ids requirements:
[
{
"requirement_uid": "req-doc-1",
"filename": "license.pdf",
"content_base64": "JVBERi0xLjRN..."
}
]| Command | Endpoint |
|---|---|
sendafrica sender-ids requirements | GET /v1/sender-ids/requirements |
sendafrica sender-ids usable | GET /v1/sender-ids/usable |
sendafrica sender-ids list | GET /v1/sender-ids |
sendafrica sender-ids create --name ... --purpose ... --sample-message ... | POST /v1/sender-ids |
sendafrica sender-ids get [id] | GET /v1/sender-ids/{id} |
sendafrica sender-ids set-default [id] | PUT /v1/sender-ids/default |
Notifications
| Command | Endpoint |
|---|---|
sendafrica notifications list | GET /v1/notifications |
sendafrica notifications get [id] | GET /v1/notifications/{id} |
sendafrica notifications unread-count | GET /v1/notifications/unread-count |
sendafrica notifications mark-read [id] | PATCH /v1/notifications/{id}/read |
sendafrica notifications read-all | POST /v1/notifications/read-all |
Support chat
| Command | Endpoint |
|---|---|
sendafrica support-chat --message "..." | POST /v1/support/chat |
The support chat command is conversational. When the API responds with confirmation_required, re-run with --confirm to proceed. Load a conversation history from a file with --chat-file (JSON array or NDJSON format).
Config
| Command | Description |
|---|---|
sendafrica config add-profile [name] --api-key ... --api-url ... | Add or update a profile |
sendafrica config use [name] | Switch the active profile |
sendafrica config list | List profiles |
sendafrica config delete [name] | Delete a profile |
sendafrica config show | Show resolved config (secrets masked) |
Pagination
List endpoints accept --page and --per-page (defaults: page 1, 50 items). Some accept additional filters:
sendafrica sms-logs --page 2 --per-page 100
sendafrica contacts list-contacts my-list-id --page 1 --per-page 200
sendafrica campaigns recipients campaign-id --status deliveredUsing the CLI from scripts and agents
The CLI is designed to be safely driven by other tools. Recommended practices:
- Always pass
-o jsonso output is structured and stable to parse. Never parse the defaulttableformat. - Prefer environment variables over flags so secrets never appear in your shell history or process list (
ps). Pass--api-urlonly when it differs from the default. - Check before you act. Run
sendafrica health,sendafrica credits-balance, andsendafrica mefirst to confirm connectivity and identity before mutating anything. - Use
--helpon any command for its exact flags:sendafrica sms-send --help. - Respect pagination and per-page caps: iterate pages rather than assuming one call returns everything.
- Treat non-zero exit codes as failures. A message that was not accepted by the provider returns a non-zero exit; check
credits-balanceafter mixed batches. - Keep the JSON envelope in mind. Responses follow the API
{success, data, error, request_id, timestamp}shape; readdatafor the result.
A minimal agent-friendly session:
export SENDAFRICA_API_URL=https://api.sendafrica.online
export SENDAFRICA_API_KEY="$SA_KEY"
sendafrica health -o json
sendafrica credits-balance -o json
sendafrica sms-send --to 0712345678 --message "Warmup ping" -o json
sendafrica sms-logs --page 1 --per-page 5 -o jsonTroubleshooting
no credentials found
No API key or JWT could be resolved. Inspect the resolved environment and profiles:
sendafrica config show
sendafrica config listMake sure at least one source is set:
export SENDAFRICA_API_KEY=SA-your-key
# or
export SENDAFRICA_JWT_TOKEN=eyJ...
# or
sendafrica config add-profile default --api-key SA-your-keyconfiguration not loaded
The config file at ~/.config/sendafrica-cli/config.json could not be read. Fix the directory permissions:
mkdir -p ~/.config/sendafrica-cli
chmod 700 ~/.config/sendafrica-cliAPI key endpoints rejecting a JWT
All sending via POST /v1/sms/ requires an API key. Configure one and switch to it:
sendafrica config add-profile work --api-key SA-your-key
sendafrica config use workWrong API URL
Point the CLI at the documented base URL explicitly:
export SENDAFRICA_API_URL=https://api.sendafrica.online
# or per-command
sendafrica --api-url https://api.sendafrica.online credits-balancePublic boundary
The documented CLI surface covers SMS, credits, rates, contacts, campaigns, sender IDs, vouchers, support chat, and profile-scoped reads. Admin endpoints, provider callbacks, and internal service credentials are not part of the CLI integration contract.