SendAfrica logoSendAfricaDocs

TypeScript SDK

npm install sendafrica — fully typed client with zero runtime dependencies, dual CJS+ESM output, phone utilities, and an SMS part calculator.


Packagesendafrica on npm · v1.0.0 · stable
Installnpm install sendafrica
RequiresNode.js 18+ (global fetch)
OutputDual CJS + ESM + .d.ts

The TypeScript SDK wraps the REST API with full type safety — typed response objects, automatic retry with exponential backoff on 429/5xx errors, local phone normalization to E.164 before any network call, and an SMS part/credit calculator. Zero runtime dependencies.

#Installation

install.sh
bash
npm install sendafrica
# or
yarn add sendafrica
pnpm add sendafrica

#Client setup

auth.ts
typescript
import { SendAfricaClient } from "sendafrica";

const client = new SendAfricaClient({
  apiKey: process.env.SENDAFRICA_API_KEY!,
  baseUrl: "https://api.sendafrica.online", // default
  timeoutMs: 15_000,                        // per-request timeout
  maxRetries: 3,                            // retries on 429/5xx (default)
  fetch: myCustomFetch,                     // inject custom fetch (Node <18)
});

#Sending SMS

send.ts
typescript
const result = await client.sendSms(
  {
    to: "0712345678",
    message: "Your OTP is 123456",
    from: "MyBrand", // optional, pre-approved sender ID
  },
  { idempotencyKey: "order-1234" }, // safe retry key
);

console.log(result.messageId);    // "SA-abc123..."
console.log(result.status);       // "sent"
console.log(result.creditsUsed);  // 1

#Phone utilities & SMS calculator

Standalone pure functions — no client needed, zero network calls:

utils.ts
typescript
import {
  normalizeTzPhone,
  isValidTzPhone,
  getSmsPartInfo,
  detectEncoding,
} from "sendafrica";

normalizeTzPhone("0712345678");       // "+255712345678"
normalizeTzPhone("+255 712 345 678"); // "+255712345678"
isValidTzPhone("+255712345678");      // true
isValidTzPhone("+254712345678");      // false (not Tanzania)

getSmsPartInfo("Hello, your order is ready.");
// { encoding: "GSM-7", length: 28, parts: 1, creditsRequired: 1 }

detectEncoding("Hello 😊");           // "UCS-2"

#Credits & payments

credits.ts
typescript
const balance = await client.getBalance();
console.log(balance.accountId, balance.balance);

const history = await client.getCreditHistory({ page: 1, perPage: 25 });

// Voucher top-up via mobile money
const rate = await client.getVoucherRate();
for (const tier of rate.tiers) {
  console.log(tier.maxAmountTzs, tier.rateTzsPerCredit);
}

const voucher = await client.createVoucher({ provider: "snippe", amount: 50000 });
console.log(voucher.id, voucher.status, voucher.creditAmount);

#Error handling

All errors inherit from SendAfricaError with typed properties .code, .httpStatus, .requestId, plus convenience getters .isInsufficientCredits, .isRateLimited, .isUnauthorized:

errors.ts
typescript
import { SendAfricaError, InvalidPhoneNumberError } from "sendafrica";

try {
  await client.sendSms({ to: "0712345678", message: "Hello" });
} catch (err) {
  if (err instanceof InvalidPhoneNumberError) {
    // show "Please check the phone number"
  } else if (err instanceof SendAfricaError) {
    if (err.isInsufficientCredits) {
      // top up credits
    } else if (err.isRateLimited) {
      // back off (SDK already retries automatically)
    } else if (err.isUnauthorized) {
      // check API key
    } else {
      console.error(err.code, err.message, err.requestId);
    }
  }
}