SwalaSMS Developer API

Swala SMS API

Build reliable messaging workflows with a clear, live reference.

Authentication, request examples, permissions, aliases, response behaviour and error handling are arranged so developers can move from first call to production without hunting through scattered notes.

Versionv1.0.0
Groups3
Endpoints9
Methods2

Introduction

SwalaSMS Developer API lets your system send and track SMS messages through the same routing, sender ID, wallet, spam-control, and country-credit rules used by the web portal.

Use a sandbox API key while testing. Sandbox keys return realistic queued/delivered responses but never send real SMS or consume SMS credits. Use a live key only after your sender IDs, SMS route, wallet balance, and provider credits are ready.

Every request must include Authorization: Bearer YOUR_API_KEY. Send endpoints also support Idempotency-Key; pass a stable unique value from your own system when retrying a request so the same SMS is not queued twice.

Phone numbers should be sent in E.164 format, for example +255712345678. A successful send returns 202 Accepted because delivery is queued and processed asynchronously. Use the status endpoint to check the current state of a message.

Common statuses are queued, processing, sent, delivered, failed, and scheduled. Validation, sender ID, route, wallet, provider-credit, spam-policy, and recipient opt-out errors return JSON with success: false and a readable message.

Authentication

Every request carries the key issued with your application as a bearer token. A key is shown once, when it is created. The base URL is:

https://swalasms.co.tz/api/v1
Authorization: Bearer swala_live_xxx

A key carries only the permissions granted to its application. Each endpoint below names the one it needs; a key without it is answered with 403.

Messaging

Send messages and check delivery and balance.

5 endpoints
GET https://swalasms.co.tz/api/v1/balance sms:read

Check SMS balance

Also answers at https://swalasms.co.tz/api/v1/sms/balance — identical behaviour.

Return the customer SMS credit balance for the API key owner.

The top-level balance is the total available SMS units across all enabled country wallets. The countries array breaks the balance down per country with wallet UID, ISO country code, calling code, and remaining SMS units.

Use this endpoint before sending if your application needs to warn users before a campaign is queued. The actual send endpoints still perform the final wallet and provider-credit checks inside a database transaction.

Example

curl -X GET https://swalasms.co.tz/api/v1/balance \
  -H "Authorization: Bearer swala_live_xxx"

Responses

200 Success
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "data": {
        "balance": "value",
        "currency": "SMS",
        "countries": [
            {
                "wallet_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
                "country": "TZ",
                "calling_code": "+255",
                "balance": "value"
            }
        ]
    }
}
POST https://swalasms.co.tz/api/v1/sms/messages sms:send

Send a single SMS

Also answers at https://swalasms.co.tz/api/v1/sms/quick-message — identical behaviour.

Queue one SMS for delivery through the active SwalaSMS routing engine, under the same rules a message gets when it is composed in the portal. Nothing is relaxed because the request arrived over the API.

The API key owner must have an active account, an approved sender ID assigned to the account, a verified SMS route for the destination country, enough customer SMS credit, and an available provider route with provider credit. The message body is checked by the spam policy before it is accepted.

Send recipient in E.164 format, for example +255712345678. Include Idempotency-Key when retrying from your application; if the same key was already accepted, SwalaSMS returns the existing message instead of creating a duplicate.

Successful requests return 202 Accepted because the SMS is queued and delivered asynchronously. The response carries the message uid for your own records; delivery outcomes arrive at your configured webhook.

For one message to many recipients, use POST /api/v1/sms/send-bulk. Repeating the same body and sender through this single-message endpoint is rate-limited.

Body

FieldTypeNotes
recipient * string max 32 characters
sender_id * string max 32 characters
body * string max 1600 characters

Example

curl -X POST https://swalasms.co.tz/api/v1/sms/messages \
  -H "Authorization: Bearer swala_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: your-unique-reference" \
  -d '{
    "recipient": "+255712345678",
    "sender_id": "YOURBRAND",
    "body": "Your order is ready for pickup."
}'

Responses

200 Success
{}
422 Validation error
{
    "message": "A human-readable description of the result.",
    "errors": {
        "recipients.0": [
            "The field is invalid."
        ]
    }
}
GET https://swalasms.co.tz/api/v1/sms/messages sms:read

List messages and their delivery status

Every message this account has sent, newest first, with what became of it.

A send returns queued: the carrier decides delivery seconds or minutes later, so this is how an integration finds out. Poll it, or read one message directly with GET /sms/messages/{uid} using the uid the send returned.

Filter with status (queued, sent, delivered, failed), channel (api or web, to separate your integration's traffic from messages staff sent in the portal), and from / to as dates. per_page defaults to 50 and is capped at 100.

Each entry carries status, queued_at, sent_at, delivered_at, failure_reason and the billed units.

Parameters

NameInTypeNotes
channel query string
status query string
from query string
to query string
per_page query integer

Example

curl -X GET https://swalasms.co.tz/api/v1/sms/messages \
  -H "Authorization: Bearer swala_live_xxx"

Responses

200 Success
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "data": [
        {
            "uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
            "recipient": "+255712345678",
            "sender_id": "SWALASMS",
            "units": 1,
            "channel": "api",
            "status": "delivered",
            "provider_message_id": "prov_abc123",
            "queued_at": "2026-08-25T09:00:00+00:00",
            "sent_at": "2026-08-25T09:00:02+00:00",
            "delivered_at": "2026-08-25T09:00:07+00:00",
            "failure_reason": null
        }
    ],
    "meta": {
        "current_page": 1,
        "per_page": 20,
        "total": 42,
        "last_page": 3
    }
}
POST https://swalasms.co.tz/api/v1/sms/send-bulk sms:send

Send bulk SMS

Queue the same SMS body to many recipients in one API call.

Use this endpoint when your application already has the recipient list and each number is ready in E.164 format. The request accepts up to 1000 recipients. SwalaSMS still creates one local message per recipient so balance, reports, idempotency and delivery webhooks keep working the same way as the portal.

The payload mirrors provider bulk APIs: send recipients, message, sender_id, optional campaign_name, and optional scheduled_at.

Successful requests return campaign-level counts. A recipient can still be skipped if its route, wallet, sender ID, provider credit or spam checks fail.

Body

FieldTypeNotes
recipients * array One to 1000 recipient phone numbers. · max 1000 items
message * string The same SMS body sent to every recipient. · max 1600 characters
sender_id * string Your approved SwalaSMS sender ID name. · max 32 characters
campaign_name optional string A name for this campaign in reports. · max 120 characters
scheduled_at optional string Future ISO-8601 date/time, or omit/null to send now.

Example

curl -X POST https://swalasms.co.tz/api/v1/sms/send-bulk \
  -H "Authorization: Bearer swala_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: your-unique-reference" \
  -d '{
    "recipients": [
        "+255755000001",
        "+255755000002",
        "+255755000003"
    ],
    "message": "Flash sale! 30% off today only.",
    "sender_id": "YOURBRAND",
    "campaign_name": "June Flash Sale",
    "scheduled_at": null
}'

Responses

200 Success
"value"
202 Accepted for delivery
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "mode": "sandbox",
    "data": {
        "uid": "4c3b2a19-5566-7788-99aa-bbccddeeff00",
        "name": "August Promo",
        "status": "queued",
        "recipient_count": 3,
        "queued_count": 3,
        "failed_count": 0,
        "scheduled_at": null,
        "failures": [],
        "results": [
            {
                "recipient": "+255755000001",
                "status": "queued",
                "message_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e"
            }
        ]
    }
}
422 Validation error
{
    "message": "A human-readable description of the result.",
    "errors": {
        "recipients.0": [
            "The field is invalid."
        ]
    }
}
GET https://swalasms.co.tz/api/v1/sms/messages/{uid} sms:read

Check one message

The delivery status of a single message, by the uid the send returned.

status moves queuedsentdelivered, or ends at failed with a failure_reason written for a person. delivered_at is set the moment the carrier confirms it. The response carries the queued, sent, delivered and failure timestamps when they are known; which provider carried the message is not exposed.

An invalid UID is answered with a validation error, and a valid one belonging to another account with 404 — the two are told apart deliberately, so a wrong id is a mistake to fix rather than a hint about somebody else's traffic.

A sandbox key answers delivered immediately without looking anything up, so an integration can be built end to end before a real message is ever sent.

Parameters

NameInTypeNotes
uid * path string

Example

curl -X GET https://swalasms.co.tz/api/v1/sms/messages/{uid} \
  -H "Authorization: Bearer swala_live_xxx"

Responses

200 Success
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "data": {
        "uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
        "recipient": "+255712345678",
        "sender_id": "SWALASMS",
        "units": 1,
        "channel": "api",
        "status": "delivered",
        "provider_message_id": "prov_abc123",
        "queued_at": "2026-08-25T09:00:00+00:00",
        "sent_at": "2026-08-25T09:00:02+00:00",
        "delivered_at": "2026-08-25T09:00:07+00:00",
        "failure_reason": null,
        "campaign_uid": "4c3b2a19-5566-7788-99aa-bbccddeeff00",
        "campaign_name": "August Promo",
        "failure_context": null,
        "delivery_report": {
            "checked_at": "2026-08-25T09:05:00+00:00",
            "status": "delivered",
            "delivery_status": "DELIVRD"
        }
    }
}
404 No such record for this account
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "message": "SMS message was not found for this API key."
}
422 The request was understood but refused
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "message": "Message UID must be a valid UUID."
}

Sender IDs

Request a sender ID and track its approval. Mirrors the web flow exactly.

3 endpoints
GET https://swalasms.co.tz/api/v1/sender-ids sender-ids:read

List sender IDs

Also answers at https://swalasms.co.tz/api/v1/sms/sender-ids — identical behaviour.

Every sender ID this account can send with, plus every request it has made, at any status.

Use this to poll a request submitted with POST /sender-ids. A sender ID may only be used for sending once is_usable is true (status is approved). When a request was turned down, rejection_reason explains why.

Statuses: pending, approved, rejected, suspended.

Example

curl -X GET https://swalasms.co.tz/api/v1/sender-ids \
  -H "Authorization: Bearer swala_live_xxx"

Responses

200 Success
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "data": [
        {
            "uid": "a1b2c3d4-1111-2222-3333-444455556666",
            "sender_id": "SWALASMS",
            "country_uid": "b2c3d4e5-2222-3333-4444-555566667777",
            "country_code": "+255",
            "country_name": "Tanzania",
            "status": "approved",
            "is_default": true,
            "is_global": false,
            "sample_message": "Your order has shipped and is on its way.",
            "is_usable": true,
            "submitted_document_uids": [
                "c3d4e5f6-3333-4444-5555-666677778888"
            ],
            "requested_at": "2026-08-01T10:00:00+00:00",
            "updated_at": "2026-08-02T08:00:00+00:00"
        }
    ]
}
POST https://swalasms.co.tz/api/v1/sender-ids sender-ids:write

Request a sender ID

Also answers at https://swalasms.co.tz/api/v1/sms/sender-ids — identical behaviour.

Submits a sender ID for manual review. Applies exactly the same gates as the web form, so anything refused here would have been refused there.

Attaching documents. Every requirement from GET /sender-ids/requirements with is_required: true must be present, either way:

  • JSON: documents_base64: [{ "requirement_uid": "…", "filename": "certificate.pdf", "content_base64": "…" }]
  • Multipart: a file part named documents[<requirement_uid>]

Accepted formats are pdf, jpg, jpeg and png, up to 5 MB each.

Errors.

Status error Meaning
422 sender_id_not_eligible Not enough SMS credits purchased yet.
422 route_not_verified No verified SMS route for that country.
422 country_not_available That country is not open on this platform.
409 sender_id_already_requested This name already exists for this country. Poll GET /sender-ids instead; retrying never succeeds.

Approval is manual. Poll GET /sender-ids until is_usable is true.

Body

FieldTypeNotes
sender_id * string
country_uid * string
sample_message * string max 500 characters
purpose * string
documents optional array or null
documents_base64 optional array or null

Example

curl -X POST https://swalasms.co.tz/api/v1/sender-ids \
  -H "Authorization: Bearer swala_live_xxx" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: your-unique-reference" \
  -d '{
    "sender_id": "YOURBRAND",
    "country_uid": "TZ",
    "sample_message": "Your order is ready for pickup.",
    "purpose": "value",
    "documents": "value",
    "documents_base64": "value"
}'

Responses

201 Response
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "message": "A human-readable description of the result.",
    "data": {
        "uid": "a1b2c3d4-1111-2222-3333-444455556666",
        "sender_id": "SWALASMS",
        "country_uid": "b2c3d4e5-2222-3333-4444-555566667777",
        "country_code": "+255",
        "country_name": "Tanzania",
        "status": "approved",
        "is_default": true,
        "is_global": false,
        "sample_message": "Your order has shipped and is on its way.",
        "is_usable": true,
        "submitted_document_uids": [
            "c3d4e5f6-3333-4444-5555-666677778888"
        ],
        "requested_at": "2026-08-01T10:00:00+00:00",
        "updated_at": "2026-08-02T08:00:00+00:00"
    }
}
409 Response
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "error": "sender_id_already_requested",
    "message": "A human-readable description of the result."
}
422 Validation error
{
    "message": "A human-readable description of the result.",
    "errors": {
        "recipients.0": [
            "The field is invalid."
        ]
    }
}
GET https://swalasms.co.tz/api/v1/sender-ids/requirements sender-ids:read

Get sender ID requirements

Also answers at https://swalasms.co.tz/api/v1/sms/sender-ids/requirements — identical behaviour.

Call this before requesting a sender ID. It returns everything that decides whether a request will be accepted, and those rules differ per platform and per account.

  • eligibility.can_requestfalse means the account has not bought enough SMS credits yet. required_units and minimum_purchase_amount say how much is needed. Do not attempt the request.
  • countries[] — the only countries this account may request for. A country appears here once a phone number has been verified for it under SMS Routes, because a sender ID is registered per country.
  • documents[] — every entry with is_required: true must be attached when you request. Use its uid.
  • sender_id_rules — 3 to 11 characters of letters, numbers and single spaces between words, and a list of reserved words that are always refused. Case is yours to choose and is carried through to the handset.
  • purposes — the allowed purpose values; send exactly one as purpose.
  • sample_message_rules — 50 to 500 characters.

Example

curl -X GET https://swalasms.co.tz/api/v1/sender-ids/requirements \
  -H "Authorization: Bearer swala_live_xxx"

Responses

200 Success
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "data": {
        "eligibility": {
            "can_request": true,
            "purchased_units": 1,
            "required_units": 50,
            "currency": "SMS",
            "minimum_purchase_amount": "2.50",
            "price_per_unit": "2.50",
            "reason": "The request could not be completed."
        },
        "countries": [
            {}
        ],
        "documents": [
            {
                "uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
                "name": "Jane Doe",
                "is_required": true,
                "accepted_formats": [
                    "pdf",
                    "jpg",
                    "jpeg",
                    "png"
                ],
                "max_size_kb": 5120
            }
        ],
        "purposes": [
            "Notification",
            "Transactional",
            "OTP / Verification",
            "Promotional",
            "Marketing",
            "Alerts & Reminders",
            "Other"
        ],
        "sender_id_rules": {
            "min_length": 3,
            "max_length": 11,
            "pattern": "^[A-Za-z0-9]+( [A-Za-z0-9]+)*$",
            "description": "Letters, numbers, and single spaces between words. No symbols or accents. Outer whitespace is trimmed, repeated spaces are collapsed, and the sender ID is stored in uppercase.",
            "reserved_names": [
                "ADMIN",
                "ALERT",
                "BANK",
                "BANKING",
                "GOV",
                "GOVT",
                "INFO",
                "LOGIN",
                "NOTICE",
                "OTP",
                "PASSWORD",
                "SECURE",
                "SECURITY",
                "SERVICE",
                "SUPPORT",
                "SYSTEM",
                "TAX",
                "UNKNOWN",
                "UNVERIFIED",
                "VERIFY",
                "WHATSAPP"
            ]
        },
        "sample_message_rules": {
            "min_length": 50,
            "max_length": 500,
            "description": "A real example of the messages you will send with this sender ID. Reviewers read it."
        }
    }
}

Account

Who the key belongs to, which routes are verified, and what is left to spend.

1 endpoint
GET https://swalasms.co.tz/api/v1/sms/profile profile:read

Read the account profile

Returns the account, its verified SMS routes and the credit balance held for each country.

Example

curl -X GET https://swalasms.co.tz/api/v1/sms/profile \
  -H "Authorization: Bearer swala_live_xxx"

Responses

200 Success
{
    "success": true,
    "request_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
    "data": {
        "uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
        "name": "Jane Doe",
        "email": "jane@example.com",
        "phone": "+255712345678",
        "phone_verified_at": "2026-08-25T09:00:00+00:00",
        "country": {
            "uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
            "iso2": "TZ",
            "calling_code": "+255",
            "name": "Jane Doe"
        },
        "balances": [
            {
                "wallet_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
                "country_uid": "9f2c1e3a-4b7d-4e2b-9c3e-2f6a7d8b1c4e",
                "country": "TZ",
                "calling_code": "+255",
                "balance": 500,
                "currency": "SMS"
            }
        ],
        "routes": "value"
    }
}
Errors

Every failure answers with JSON carrying success: false, the request_uid for the attempt, and a message written for a person. Quote the request_uid when contacting support.

StatusMeaningWhat to do
401Key missing, expired or revokedIssue a new key for the application
403Key lacks the permission the endpoint needsGrant the scope in application settings
422Understood but refused — no credit, unapproved sender ID, unverified route, spam policyRead message; it names the cause
429Too many requestsBack off and retry; the limit is per key per minute