Developers

API reference

Send OTPs, alerts and campaigns from your own software. JSON in, JSON out, bearer-key authentication.

Base URL

https://sms.allotropicltd.com/api/public/v1

Authentication

Create a key on the Developers page and send it as a bearer token on every request. Keys are shown once; store them securely and revoke them if exposed.

Authorization: Bearer alt_live_xxxxxxxx

Phone numbers

Local numbers default to Tanzania (+255): 0712345678 becomes 255712345678.

Numbers in other countries must be international: 2547…, +44…, 00971…. Invalid and duplicate numbers are returned in rejected and are not charged.

Billing and message parts

1 credit = 1 SMS part. Standard (GSM-7) messages fit 160 characters in one part, or 153 per part when longer. Unicode messages (emoji, non-Latin scripts) fit 70, or 63 per part when longer.

Credits are reserved when a request is accepted. Messages that fail permanently, and cancelled scheduled messages, are refunded automatically.

Sent vs delivered

queued: accepted and waiting to be sent (including scheduled and retrying messages).

sent: handed to the mobile network. This is not proof the handset received it.

delivered: the network confirmed delivery to the handset. Delivery confirmations are being rolled out; until then most messages will finish as sent.

failed: could not be sent after retries; credits refunded. cancelled: a scheduled message cancelled before sending.

Scheduling and retries

Pass schedule_at (ISO 8601, UTC recommended). Temporary failures retry automatically with growing delays (2, 4, 8, 16 minutes) up to 5 attempts.

Idempotency

Send an Idempotency-Key header (any unique string) on send calls. Repeating a request with the same key returns the original campaign with duplicate: true and is not charged again.

Rate limits and pagination

Each API key may make 120 requests per minute; beyond that you receive HTTP 429. Lists use page and per_page (max 500) and return meta.total.

Send SMS

POST/sms/send

Send the same message to one or many numbers (up to 10,000). to accepts a string, a comma-separated string or an array.

  • sender_id (string, required, max 13): an approved sender ID on your account
  • to (string | string[], required): local Tanzanian (0712345678) or international (255712345678 / +255712345678) numbers
  • message (string, required, max 1600 characters)
  • schedule_at (ISO 8601, optional): send later; must be at least 30 seconds in the future
  • reference (string, optional, max 120): your campaign label
{
  "sender_id": "MYBRAND",
  "to": ["255712345678", "0754000000"],
  "message": "Your verification code is 4821",
  "schedule_at": "2026-12-01T08:00:00Z",
  "reference": "December promo"
}
{
  "success": true,
  "data": {
    "campaign_id": "6f1c2a8e-…",
    "status": "scheduled",
    "accepted": 2,
    "rejected": [],
    "credits_used": 2,
    "balance": 998,
    "duplicate": false
  }
}
curl -X POST https://sms.allotropicltd.com/api/public/v1/sms/send \
  -H "Authorization: Bearer alt_live_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1042" \
  -d '{"sender_id":"MYBRAND","to":"255712345678","message":"Your code is 4821"}'

Send personalised bulk

POST/sms/bulk

Different text per recipient in one request (up to 10,000 items).

  • sender_id (string, required)
  • messages (array, required): items of { "to": string, "message": string }
  • schedule_at, reference: as for Send SMS
{
  "sender_id": "MYBRAND",
  "messages": [
    { "to": "255712345678", "message": "Hi Neema, your order has shipped" },
    { "to": "255754000000", "message": "Hi Juma, your order has shipped" }
  ]
}
{ "success": true, "data": { "campaign_id": "…", "status": "completed", "accepted": 2, "rejected": [], "credits_used": 2, "balance": 996, "duplicate": false } }

Message or campaign status

GET/sms/{id}

Pass a message ID or a campaign ID returned by a send call. Campaigns include up to 1,000 messages.

{ "success": true, "data": { "id": "…", "campaign_id": "…", "to": "255712345678", "sender_id": "MYBRAND", "message": "…", "parts": 1, "encoding": "GSM7", "status": "sent", "error": null, "created_at": "…", "sent_at": "…", "delivered_at": null } }

Reports

GET/reports

Paginated message list, newest first.

  • status: queued | sent | delivered | failed | cancelled
  • to: phone number contains digits
  • sender_id, campaign_id
  • from, to_date: ISO dates
  • page (default 1), per_page (default 100, max 500)
{ "success": true, "data": [ … ], "meta": { "page": 1, "per_page": 100, "total": 532 } }

Balance

GET/balance

Remaining SMS credits.

{ "success": true, "data": { "credits": 998 } }

Sender IDs

GET/sender-ids

Active sender IDs approved for your account.

{ "success": true, "data": [ { "sender_id": "MYBRAND", "default": true } ] }

Webhooks

Set an https URL on the Developers page. We POST final status changes (sent, failed) in batches. Respond with any 2xx within 5 seconds; delivery is best effort and not retried.

POST https://your-app.example.com/sms-status
Content-Type: application/json
User-Agent: AllotropicSMS-Webhook/1.0

{ "event": "message.status", "data": [ { "id": "…", "to": "255712345678", "status": "sent", "error": null } ] }

Errors

{ "success": false, "error": { "code": "INSUFFICIENT_CREDITS", "message": "…" } }
400INVALID_JSONBody is not valid JSON
401UNAUTHORIZEDMissing, invalid or revoked API key
402INSUFFICIENT_CREDITSNot enough credits for this request
403INVALID_SENDER_IDSender ID is not approved for your account
403ACCOUNT_SUSPENDEDAccount is suspended
404NOT_FOUNDMessage or campaign not found
422VALIDATION_ERRORRequest body or query is invalid
422NO_VALID_RECIPIENTSNone of the numbers are valid
429RATE_LIMITEDMore than 120 requests per minute per key
500INTERNALUnexpected error: retry with the same Idempotency-Key