Skip to Content
CLI & local AgentSendAfrica CLI

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@latest

Verify the install:

sendafrica --help sendafrica health

Security model

Credentials are never loaded from .env files. They come from exactly three places, in priority order (highest wins):

  1. CLI flags — --api-key, --token, --api-url
  2. Environment variables — SENDAFRICA_API_KEY, SENDAFRICA_JWT_TOKEN, SENDAFRICA_API_URL, SENDAFRICA_OUTPUT, SENDAFRICA_PROFILE
  3. Config profile — the currently active profile in ~/.config/sendafrica-cli/config.json

Other guarantees:

  • The config file is written with 0600 permissions and its directory with 0700.
  • sendafrica config show masks 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-bulk accepts 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 masked

Quick 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 pairs
  • json — structured output for scripts and agents
  • yaml — YAML output
sendafrica credits-balance -o json sendafrica sms-logs -o json --page 1 --per-page 100 sendafrica vouchers rate -o json

Command reference

Public (no auth)

CommandEndpoint
sendafrica healthGET /health
sendafrica packagesGET /v1/packages
sendafrica templatesGET /v1/templates
sendafrica ratesGET /v1/rates
sendafrica rates --country TZGET /v1/rates/TZ

Authentication

CommandEndpointAuth
sendafrica register --email ... --password ... --first-name ... --last-name ...POST /v1/auth/registerNone
sendafrica login --email ... --password ...POST /v1/auth/loginNone
sendafrica refreshPOST /v1/auth/refreshRefresh token
sendafrica logoutPOST /v1/auth/logoutJWT
sendafrica verify-email --email ... --otp ...POST /v1/auth/verify-emailNone
sendafrica send-verification-email --email ...POST /v1/auth/send-verification-emailNone
sendafrica reset-password --email ...POST /v1/auth/reset-passwordNone
sendafrica reset-password-confirm --email ... --otp ... --password ...POST /v1/auth/reset-password-confirmNone
sendafrica meGET /v1/auth/meJWT/API key
sendafrica me --update --name ...PUT /v1/auth/meJWT/API key
sendafrica change-password --current-password ... --new-password ...POST /v1/auth/change-passwordJWT
sendafrica verify-phone --phone ... --otp ...POST /v1/auth/verify-phoneJWT
sendafrica send-phone-otp --phone ...POST /v1/auth/send-phone-otpJWT

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)

CommandEndpoint
sendafrica api-keys listGET /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

CommandEndpointAuth
sendafrica sms-send --to ... --message ...POST /v1/sms/API key
sendafrica sms-bulk --to ... --message ...POST /v1/sms/bulkJWT/API key
sendafrica sms-logsGET /v1/sms/logsJWT/API key

POST /v1/sms/ requires an API key; use sms-bulk if only a JWT is available.

Credits

CommandEndpoint
sendafrica credits-balanceGET /v1/credits/balance
sendafrica credits-historyGET /v1/credits/history

Contacts

CommandEndpoint
sendafrica contacts listGET /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-statusGET /v1/contact-lists/google/status
sendafrica contacts google-syncPOST /v1/contact-lists/google/sync
sendafrica contacts google-disconnectDELETE /v1/contact-lists/google/disconnect

Campaigns

CommandEndpoint
sendafrica campaigns listGET /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)

CommandEndpoint
sendafrica vouchers rateGET /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.json

documents.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..." } ]
CommandEndpoint
sendafrica sender-ids requirementsGET /v1/sender-ids/requirements
sendafrica sender-ids usableGET /v1/sender-ids/usable
sendafrica sender-ids listGET /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

CommandEndpoint
sendafrica notifications listGET /v1/notifications
sendafrica notifications get [id]GET /v1/notifications/{id}
sendafrica notifications unread-countGET /v1/notifications/unread-count
sendafrica notifications mark-read [id]PATCH /v1/notifications/{id}/read
sendafrica notifications read-allPOST /v1/notifications/read-all

Support chat

CommandEndpoint
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

CommandDescription
sendafrica config add-profile [name] --api-key ... --api-url ...Add or update a profile
sendafrica config use [name]Switch the active profile
sendafrica config listList profiles
sendafrica config delete [name]Delete a profile
sendafrica config showShow 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 delivered

Using the CLI from scripts and agents

The CLI is designed to be safely driven by other tools. Recommended practices:

  • Always pass -o json so output is structured and stable to parse. Never parse the default table format.
  • Prefer environment variables over flags so secrets never appear in your shell history or process list (ps). Pass --api-url only when it differs from the default.
  • Check before you act. Run sendafrica health, sendafrica credits-balance, and sendafrica me first to confirm connectivity and identity before mutating anything.
  • Use --help on 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-balance after mixed batches.
  • Keep the JSON envelope in mind. Responses follow the API {success, data, error, request_id, timestamp} shape; read data for 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 json

Troubleshooting

no credentials found

No API key or JWT could be resolved. Inspect the resolved environment and profiles:

sendafrica config show sendafrica config list

Make 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-key

configuration 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-cli

API 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 work

Wrong 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-balance

Public 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.

Last updated on