Developer Reference

eHub SMS REST API

Send SMS, manage sender IDs, and check your wallet programmatically. All endpoints require HMAC-signed requests.

BASE URL https://sms.ehub.co.tz/api/v1

Overview

Everything you need to integrate eHub SMS into your application.

💡
To get your API key and secret, log in to your dashboard → Developer → API Keys → Generate new key. Both values are shown only once at creation — store them securely.

The eHub SMS API is a REST API that accepts JSON request bodies and returns JSON responses. Every request must be authenticated with a Bearer token and signed with an HMAC-SHA256 signature to prevent replay attacks.

Authentication

Every request requires your API key in the Authorization header.

Required header
Authorization: Bearer YOUR_API_KEY

Your API key identifies your account. Keep it secret — never expose it in client-side code or public repositories. All requests must also include a valid HMAC signature (see below).

Request Signing

Every request must be signed with HMAC-SHA256 using your API secret.

⚠️
Requests without a valid signature or with a timestamp older than 5 minutes will be rejected with 401 Unauthorized.

Signing algorithm

Signature payload (newline-separated)
# Concatenate these four values with newlines
{unix_timestamp}
{HTTP_METHOD}
{/full/path}
{raw_body}

# Signature
signature = HMAC-SHA256(secret, payload)  # hex-encoded

Required headers

HeaderValueNotes
X-TimestampUnix timestamp (integer)Must be within ±5 min of server time
X-SignatureHMAC-SHA256 hex stringSigned with your API secret
ℹ️
For GET requests, use an empty string "" as the body. Timestamps use Unix time (seconds since epoch) — timezone doesn't matter.

Code examples

// PHP signing example
$apiKey  = 'sk_your_api_key';
$secret  = 'your_api_secret';
$method  = 'POST';
$path    = '/api/v1/sms/send';
$body    = json_encode(['to' => '255755000000', 'message' => 'Hello!', 'sender_id' => 'uuid-here']);

$timestamp = time();
$payload   = $timestamp . "\n" . $method . "\n" . $path . "\n" . $body;
$signature = hash_hmac('sha256', $payload, $secret);

$response = Http::withHeaders([
    'Authorization' => 'Bearer ' . $apiKey,
    'X-Timestamp'   => $timestamp,
    'X-Signature'   => $signature,
    'Content-Type'  => 'application/json',
])->post('https://sms.ehub.co.tz/api/v1/sms/send', json_decode($body, true));
# Python signing example
import hmac, hashlib, time, json, requests

api_key = 'sk_your_api_key'
secret  = 'your_api_secret'
method  = 'POST'
path    = '/api/v1/sms/send'
body    = json.dumps({'to': '255755000000', 'message': 'Hello!', 'sender_id': 'uuid-here'})

timestamp = str(int(time.time()))
payload   = f"{timestamp}\n{method}\n{path}\n{body}"
signature = hmac.new(secret.encode(), payload.encode(), hashlib.sha256).hexdigest()

response = requests.post(
    'https://sms.ehub.co.tz/api/v1/sms/send',
    headers={
        'Authorization': f'Bearer {api_key}',
        'X-Timestamp':   timestamp,
        'X-Signature':   signature,
        'Content-Type':  'application/json',
    },
    data=body
)
// Go signing example
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "net/http"
    "strings"
    "strconv"
    "time"
)

func sign(secret, method, path, body string) (string, string) {
    ts := strconv.FormatInt(time.Now().Unix(), 10)
    payload := ts + "\n" + method + "\n" + path + "\n" + body
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(payload))
    return ts, hex.EncodeToString(mac.Sum(nil))
}

func main() {
    apiKey := "sk_your_api_key"
    secret := "your_api_secret"
    body   := `{"to":"255755000000","message":"Hello!","sender_id":"uuid-here"}`

    ts, sig := sign(secret, "POST", "/api/v1/sms/send", body)

    req, _ := http.NewRequest("POST",
        "https://sms.ehub.co.tz/api/v1/sms/send",
        strings.NewReader(body))

    req.Header.Set("Authorization", "Bearer "+apiKey)
    req.Header.Set("X-Timestamp", ts)
    req.Header.Set("X-Signature", sig)
    req.Header.Set("Content-Type", "application/json")

    resp, err := http.DefaultClient.Do(req)
    fmt.Println(resp.Status, err)
}
// Java signing example
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.*;
import java.time.Instant;

public class EhubSmsClient {

    static String sign(String secret, String method, String path, String body) throws Exception {
        String ts      = String.valueOf(Instant.now().getEpochSecond());
        String payload = ts + "\n" + method + "\n" + path + "\n" + body;

        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(secret.getBytes(), "HmacSHA256"));
        byte[] raw = mac.doFinal(payload.getBytes());

        StringBuilder hex = new StringBuilder();
        for (byte b : raw) hex.append(String.format("%02x", b));
        return ts + ":" + hex;   // returns "timestamp:signature"
    }

    public static void main(String[] args) throws Exception {
        String apiKey = "sk_your_api_key";
        String secret = "your_api_secret";
        String body   = "{\"to\":\"255755000000\",\"message\":\"Hello!\",\"sender_id\":\"uuid-here\"}";

        String[] parts = sign(secret, "POST", "/api/v1/sms/send", body).split(":", 2);
        String ts  = parts[0];
        String sig = parts[1];

        HttpRequest req = HttpRequest.newBuilder()
            .uri(URI.create("https://sms.ehub.co.tz/api/v1/sms/send"))
            .header("Authorization", "Bearer " + apiKey)
            .header("X-Timestamp",   ts)
            .header("X-Signature",   sig)
            .header("Content-Type",  "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(body))
            .build();

        HttpResponse<String> resp =
            HttpClient.newHttpClient().send(req, HttpResponse.BodyHandlers.ofString());
        System.out.println(resp.body());
    }
}
# cURL signing example (bash)
API_KEY="sk_your_api_key"
SECRET="your_api_secret"
BODY='{"to":"255755000000","message":"Hello!","sender_id":"uuid-here"}'
TS=$(date +%s)
PAYLOAD="${TS}
POST
/api/v1/sms/send
${BODY}"
SIG=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')

curl -s -X POST https://sms.ehub.co.tz/api/v1/sms/send \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Signature: $SIG" \
  -H "Content-Type: application/json" \
  -d "$BODY"

Rate Limiting

120 requests per minute per API key.

Every response includes rate limit headers so you can track your usage in real time. When the limit is exceeded, the API returns 429 Too Many Requests with a retry_after field.

HeaderDescription
X-RateLimit-LimitMax requests per minute for your key
X-RateLimit-RemainingRequests remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets

Response Format

All responses are JSON with a consistent envelope.

{
  "success":   true,
  "message":   "SMS sent successfully",
  "data":      { /* endpoint-specific payload */ },
  "timestamp": "2026-06-05T10:00:00+03:00"
}
Error response
{
  "success":   false,
  "message":   "Invalid sender_id.",
  "timestamp": "2026-06-05T10:00:00+03:00"
}

Error Codes

Standard HTTP status codes with descriptive messages.

StatusMeaning
401 Missing/invalid API key · Expired timestamp · Invalid HMAC signature · Inactive account
403 Sender ID not found or not accessible · Sender ID not yet approved
409 Duplicate sender ID — you have already registered this name
422 Validation failed — check the errors field for details
429 Rate limit exceeded — wait until the window resets (retry_after seconds)
500 Internal server error — contact support if this persists

Sender IDs

Manage the alphanumeric names that appear as the SMS sender.

GET /sender-ids List sender IDs
Returns all sender IDs available to your account: your own (any status), sender IDs shared with you by an admin, and public approved sender IDs. Use the id (UUID) field when sending SMS.
GET /api/v1/sender-ids
Authorization: Bearer sk_...
X-Timestamp: 1780658993
X-Signature: abc123...
{
  "success": true,
  "data": {
    "own": [
      {
        "id":          "00420892-38bd-47b0-9a5f-ea55bef5d2d1",
        "sender_name": "MICHANGO",
        "status":      "approved",
        "purpose":     "TRANSACTIONAL",
        "is_default":  false,
        "type":        "own",
        "created_at":  "2026-01-27T14:40:26+03:00"
      }
    ],
    "shared": [],
    "public": []
  }
}
POST /sender-ids Request sender ID
Submit a new sender ID for admin approval. The sender ID must be 4–11 alphanumeric characters. Once approved you can use it to send SMS.
Request body
FieldTypeDescription
sender_idstringrequired4–11 alphanumeric characters, will be uppercased
purposestringoptionalIntended use, e.g. "Marketing promotions"
POST /api/v1/sender-ids
Authorization: Bearer sk_...
X-Timestamp: 1780658993
X-Signature: abc123...
Content-Type: application/json

{
  "sender_id": "MYBRAND",
  "purpose":   "Promotional messages"
}
{
  "success": true,
  "message": "Sender ID submitted for approval.",
  "data": {
    "id":          "e47c45f3-43ce-4bcc-b868-9db92840f11f",
    "sender_name": "MYBRAND",
    "status":      "pending",
    "purpose":     "Promotional messages",
    "created_at":  "2026-06-05T14:33:52+03:00"
  }
}

SMS

Send single or bulk SMS messages to Tanzanian phone numbers.

POST /sms/send Send single SMS
Send a single SMS message immediately. Balance is deducted before sending and refunded automatically on failure.
Request body
FieldTypeDescription
tostringrequiredRecipient phone number (e.g. 255755000000 or 0755000000)
messagestringrequiredSMS body text, max 640 characters (4 SMS parts)
sender_iduuidrequiredUUID of an approved sender ID from GET /sender-ids
POST /api/v1/sms/send
Authorization: Bearer sk_...
X-Timestamp: 1780658993
X-Signature: abc123...
Content-Type: application/json

{
  "to":        "255755957514",
  "message":   "Your verification code is 123456",
  "sender_id": "00420892-38bd-47b0-9a5f-ea55bef5d2d1"
}
{
  "success": true,
  "message": "SMS sent successfully",
  "data": {
    "message_id": "2d853818-5529-4838-bee0-1cb012b1a136",
    "to":         "255755957514",
    "status":     "sent",
    "cost":       "25.00",
    "parts":      1,
    "created_at": "2026-06-05T13:45:33+03:00"
  }
}
POST /sms/send-bulk Send bulk SMS
Send the same message to multiple recipients in one request. Creates a campaign and dispatches messages via the queue. Supports scheduling.
Request body
FieldTypeDescription
recipientsarrayrequiredArray of phone numbers, max 1000. Duplicates are removed.
messagestringrequiredSMS body text, max 640 characters
sender_iduuidrequiredUUID of an approved sender ID
campaign_namestringoptionalLabel for this campaign, max 255 characters
scheduled_atdatetimeoptionalISO 8601 future datetime to schedule the send
POST /api/v1/sms/send-bulk
Content-Type: application/json

{
  "recipients":    ["255755000001", "255755000002", "255755000003"],
  "message":        "Flash sale! 30% off today only.",
  "sender_id":      "00420892-38bd-47b0-9a5f-ea55bef5d2d1",
  "campaign_name":  "June Flash Sale",
  "scheduled_at":   null
}
{
  "success": true,
  "message": "Bulk SMS campaign created successfully",
  "data": {
    "campaign_id":       "f84e8878-27be-4bce-aac8-98e1b3ec19a0",
    "name":              "June Flash Sale",
    "total_recipients":  3,
    "total_cost":        "75.00",
    "status":            "processing",
    "scheduled_at":      null,
    "created_at":        "2026-06-05T13:45:57+03:00"
  }
}
GET /sms/history SMS history
Paginated list of SMS messages sent from your account.
Query parameters
ParameterTypeDescription
statusstringoptionalpending · queued · sent · delivered · failed
from_datedateoptionalFilter from date (YYYY-MM-DD)
to_datedateoptionalFilter to date (YYYY-MM-DD)
limitintegeroptionalResults per page, max 100. Default: 20
pageintegeroptionalPage number. Default: 1
GET /api/v1/sms/history?status=delivered&limit=20&page=1
Authorization: Bearer sk_...
X-Timestamp: 1780658993
X-Signature: abc123...
{
  "success": true,
  "data": {
    "messages": [
      {
        "message_id":   "2d853818-5529-4838-bee0-1cb012b1a136",
        "to":           "255755957514",
        "message":      "Your code is 123456",
        "sender_id":    "MICHANGO",
        "status":       "delivered",
        "cost":         "25.00",
        "parts":        1,
        "sent_at":      "2026-06-05T13:45:35+03:00",
        "delivered_at": "2026-06-05T13:45:37+03:00",
        "created_at":   "2026-06-05T13:45:33+03:00"
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page":     20,
      "total":        142,
      "last_page":    8
    }
  }
}
GET /sms/{message_id} Message status
Get the current delivery status of a single message by its UUID.
GET /api/v1/sms/2d853818-5529-4838-bee0-1cb012b1a136
{
  "success": true,
  "data": {
    "message_id":   "2d853818-5529-4838-bee0-1cb012b1a136",
    "to":           "255755957514",
    "status":       "delivered",
    "cost":         "25.00",
    "sent_at":      "2026-06-05T13:45:35+03:00",
    "delivered_at": "2026-06-05T13:45:37+03:00",
    "error_message": null,
    "created_at":   "2026-06-05T13:45:33+03:00"
  }
}

Wallet

Check your SMS balance and transaction history.

GET /wallet/balance Get balance
Returns your current SMS credit balance.
GET /api/v1/wallet/balance
{
  "success": true,
  "data": {
    "balance":     "0.00",
    "sms_balance": 78,
    "currency":    "TZS",
    "updated_at":  "2026-06-05T13:45:16+03:00"
  }
}
GET /wallet/transactions Transactions
Paginated list of debit/credit transactions on your account.
Query parameters
ParameterTypeDescription
limitintegeroptionalResults per page, max 100. Default: 20
pageintegeroptionalPage number. Default: 1
GET /api/v1/wallet/transactions?limit=20&page=1
Authorization: Bearer sk_...
X-Timestamp: 1780658993
X-Signature: abc123...
{
  "success": true,
  "data": {
    "transactions": [
      {
        "id":          "txn_9c3a1d88-f401-4b2a-91c3-d7ec1e028f3b",
        "type":        "debit",
        "amount":      "25.00",
        "description": "SMS to 255755957514",
        "balance":     "1950.00",
        "created_at":  "2026-06-05T13:45:33+03:00"
      },
      {
        "id":          "txn_2aa7e1b3-0c43-4d19-b98c-0fa3c9b23ef0",
        "type":        "credit",
        "amount":      "5000.00",
        "description": "Wallet top-up via Snippe",
        "balance":     "1975.00",
        "created_at":  "2026-06-04T09:12:05+03:00"
      }
    ],
    "pagination": {
      "current_page": 1,
      "per_page":     20,
      "total":        47,
      "last_page":    3
    }
  }
}