Developers

SMS API Documentation

Integrate SMS capabilities into your applications, billing system, or website with our RESTful API. Send single or bulk messages, check delivery status, and manage your account programmatically.

Version 1.0.0 REST API HTTPS Secure JSON Responses

Introduction

The 4K Net SMS API lets you send SMS messages programmatically from your own application, billing system, or captive/hotspot portal. It's RESTful, uses JSON for request and response bodies on the main endpoints, and supports both single and bulk messaging. A legacy-compatible Billing Gateway route is also available for older systems (PHPNuxBill, WHMCS, Mikrotik hotspot billing) that can only call a fixed URL.

Pro Tip: every request must use HTTPS. Include your API key either as a Bearer Authorization header (recommended) or as an api_key value in the request body/query string.

Authentication

Every endpoint below (except Test Connection) requires your API key. There are two accepted forms — use whichever your integration supports:

Header (JSON endpoints)

Authorization: Bearer YOUR_API_KEY

Query/body param (billing systems)

?api_key=YOUR_API_KEY

Your API key is generated automatically when you create an account, and can be rotated or revoked any time on your Developer page.

Base URL

All API requests should be made to:

https://sms.4knet.co.ke/api/v1/

For example, to check your balance you'd call https://sms.4knet.co.ke/api/v1/balance.

Machine-readable OpenAPI JSON is available at https://sms.4knet.co.ke/openapi.json.

All Endpoints

MethodEndpointDescription
GET/api/v1/testTest API connectivity (no auth required)
POST / GET/api/v1/sms/sendSend a single SMS message
POST/api/v1/sms/send-bulkSend SMS to multiple recipients (max 100/request)
POST / GET/api/v1/sms/statusCheck the delivery status of a sent message
GET/api/v1/balanceGet your account balance and default sender ID
GET/api/v1/sender-idsList your Sender ID applications and approvals
POST/api/v1/sender-ids/applyApply for a Sender ID
POST/api/v1/sender-ids/defaultSet an approved Sender ID as your default
GET/api/v1/wallet/balanceGet available, reserved, and effective wallet balance
GET/api/v1/wallet/ledgerGet paginated wallet ledger entries
POST / GET/api/v1/billing-gatewayLegacy billing-system integration (PHPNuxBill/WHMCS compatible)

Rate limit: 60 requests/minute per API key across all endpoints above.

Send SMS

Send a single SMS message to one recipient.

POST /api/v1/sms/send

Request Parameters

ParameterTypeRequiredDescription
recipientStringYesKenyan phone number (2547XXXXXXXX, 2541XXXXXXXX, or 07XXXXXXXX — auto-normalized)
messageStringYesSMS content, up to 1600 characters (160 = 1 unit, then ~153/unit)
sender_idStringOptionalApproved Sender ID. If omitted, your default approved Sender ID is used.
scheduled_atDate-timeOptionalFuture ISO timestamp. Wallet balance is reserved until processing.

For POST requests, include an optional Idempotency-Key header to safely retry without double-charging. See Idempotency.

Response Example (Success)

{
  "success": true,
  "message": "SMS message successfully dispatched downstream.",
  "recipient": "254712345678",
  "units": 1,
  "cost": 0.29,
  "new_balance": 92.71,
  "bytewave_uid": "8f2c1a9e0b3d4c5f"
}
curl -X POST https://sms.4knet.co.ke/api/v1/sms/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"recipient":"254712345678","message":"Hello from cURL"}'

GET Variant (billing/hotspot systems)

For systems that can only call a fixed URL with placeholder substitution, no custom headers:

GET https://sms.4knet.co.ke/api/v1/sms/send?api_key=YOUR_API_KEY&param_number=254712345678&param_text=Hello

Bulk SMS

Send the same message to multiple recipients in one call (max 100 per request). Each recipient is charged and logged individually, exactly as if you'd called Send SMS once per number.

POST /api/v1/sms/send-bulk

Request Parameters

ParameterTypeRequiredDescription
recipientsArrayYesArray of phone numbers, max 100 entries
messageStringYesSMS content sent to every recipient

Request Example

{
  "recipients": ["254712345678", "254700000001"],
  "message": "Special offer just for you!"
}

Response Example

{
  "success": true,
  "message": "Bulk SMS sent: 2 successful, 0 failed",
  "statistics": {
    "total_attempted": 2,
    "successful": 2,
    "failed": 0,
    "invalid_numbers": 0
  },
  "units_used": 2,
  "total_cost": 0.58,
  "new_balance": 92.13,
  "results": [
    { "recipient": "254712345678", "message_id": "8f2c1a9e0b3d4c5f", "success": true },
    { "recipient": "254700000001", "message_id": "a41e9b2c7d6f0a1b", "success": true }
  ],
  "invalid_numbers": [],
  "failed_recipients": {}
}
curl -X POST https://sms.4knet.co.ke/api/v1/sms/send-bulk \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"recipients":["254712345678","254700000001"],"message":"Special offer just for you!"}'

Check Status

Look up the delivery status of a message you previously sent through the API.

POST / GET /api/v1/sms/status

Request Parameters

ParameterTypeRequiredDescription
message_idStringYesThe bytewave_uid returned from Send SMS or Bulk SMS

Response Example

{
  "success": true,
  "message_id": "8f2c1a9e0b3d4c5f",
  "recipient": "254712345678",
  "sender_id": "BytewaveSMS",
  "status": "Delivered",
  "cost": 0.29,
  "created_at": "2026-07-25 11:21:56",
  "delivered_at": "2026-07-25 11:22:03",
  "message_preview": "Hello from cURL"
}
curl "https://sms.4knet.co.ke/api/v1/sms/status?api_key=YOUR_API_KEY&message_id=8f2c1a9e0b3d4c5f"

Status is one of Pending, Sent, Delivered, Failed, or Expired. delivered_at is null until a delivery receipt has arrived.

Get Balance

Get your current account balance, default sender ID, and per-unit rate.

GET /api/v1/balance

Response Example

{
  "success": true,
  "user_id": 12,
  "name": "Jane Wanjiru",
  "email": "jane@example.com",
  "sms_balance": 92.13,
  "default_sender_id": "BytewaveSMS",
  "sms_cost_per_unit": 0.29,
  "timestamp": "2026-07-25T11:21:55.000Z"
}
curl -H "Authorization: Bearer YOUR_API_KEY" https://sms.4knet.co.ke/api/v1/balance

Sender IDs

Manage the alphanumeric Sender IDs your API key is allowed to use. A custom Sender ID must be approved before it can be used for sending.

MethodEndpointPurpose
GET/api/v1/sender-idsList your Sender IDs with approval/default status
POST/api/v1/sender-ids/applySubmit a new Sender ID application
POST/api/v1/sender-ids/defaultSet an approved Sender ID as default
curl -H "Authorization: Bearer YOUR_API_KEY" https://sms.4knet.co.ke/api/v1/sender-ids
curl -X POST https://sms.4knet.co.ke/api/v1/sender-ids/apply \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sender-id-application-001" \
  -d '{"sender_id":"MYBRAND"}'
{
  "success": true,
  "message": "Sender ID application submitted.",
  "data": {
    "sender_id": "MYBRAND",
    "status": "Pending Review",
    "is_default": 0
  }
}

Wallet

Use the wallet endpoints for read-only balance and ledger checks. Top-ups and payment gateway settings are handled in the web dashboard, not through public API calls.

Balance

GET /api/v1/wallet/balance

Ledger

GET /api/v1/wallet/ledger?limit=50&offset=0
{
  "success": true,
  "message": "Wallet balance retrieved.",
  "data": {
    "available_balance": 92.13,
    "reserved_balance": 0,
    "effective_balance": 92.13
  }
}
curl -H "Authorization: Bearer YOUR_API_KEY" https://sms.4knet.co.ke/api/v1/wallet/ledger?limit=25&offset=0

Test Connection

Test API connectivity and service status.

GET /api/v1/test — no authentication required
{
  "success": true,
  "status": "online",
  "service": "4K Net Bulk SMS API",
  "version": "1.0.0",
  "database": "connected",
  "default_sender_id": "BytewaveSMS",
  "timestamp": "2026-07-25T11:21:55.000Z"
}
curl https://sms.4knet.co.ke/api/v1/test

Billing Gateway

A drop-in compatible endpoint for legacy billing/hotspot systems (PHPNuxBill, WHMCS SMS modules, Mikrotik) that call a fixed URL with their own parameter names. Accepts both GET and POST, with parameters in the URL or the request body.

POST / GET /api/v1/billing-gateway

Supported Parameter Aliases

ParameterAliasesRequiredDescription
api_keysecretYesYour API key
recipientnumber, param_number, toYesRecipient phone number
messagetext, param_textYesSMS message content
formatOptionalResponse format: simple (default) or json

Request Examples

GET /api/v1/billing-gateway?api_key=YOUR_API_KEY&param_number=254712345678&param_text=Your+bill+is+ready
POST /api/v1/billing-gateway
Content-Type: application/x-www-form-urlencoded

api_key=YOUR_API_KEY&number=254712345678&text=Your+bill+is+ready

Response Examples

Simple format (default) — OK|message_id|recipient|units_used|new_balance

OK|8f2c1a9e0b3d4c5f|254712345678|1|92.13

JSON format (?format=json)

{
  "status": "success",
  "message": "SMS message successfully dispatched downstream.",
  "data": {
    "message_id": "8f2c1a9e0b3d4c5f",
    "recipient": "254712345678",
    "units_used": 1,
    "new_balance": 92.13
  }
}

Error response (either format)

ERROR|Recipient must be a valid Kenyan phone number.

Idempotency

For retry-safe chargeable actions, send a unique Idempotency-Key header. It is currently supported on POST /api/v1/sms/send and POST /api/v1/sender-ids/apply.

Retry ScenarioResult
Same key and same request bodyOriginal response is returned with X-Cache-Lookup: HIT-IDEMPOTENT
Same key with a different bodyHTTP 409 with IDEMPOTENCY_KEY_REUSED
Same key while first request is still processingHTTP 409 with IDEMPOTENCY_REQUEST_IN_PROGRESS
curl -X POST https://sms.4knet.co.ke/api/v1/sms/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-100045-sms-1" \
  -d '{"recipient":"254712345678","message":"Payment received"}'

Webhook Payload & Signature Verification

Configure a webhook URL on your Developer page to get notified the instant a message you sent through the API changes status — no polling Check Status required.

// POST to your configured webhook URL, whenever a message sent via the API changes status
{
  "event": "sms.status_updated",
  "message_id": "8f2c1a9e0b3d4c5f",
  "phone": "254712345678",
  "status": "Delivered",
  "timestamp": "2026-07-25T11:22:03.000Z"
}
// Node.js — verifying X-Webhook-Signature
const crypto = require("crypto");

function isValidSignature(rawBody, signatureHeader, secret) {
  const expected = "sha256=" + crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));
}

Error Codes

HTTP CodeError TypeDescription
400Bad RequestInvalid parameters, bad phone number, message too long, or insufficient balance
401UnauthorizedMissing, invalid, or revoked API key
403ForbiddenAccount is suspended
404Not FoundLinked account or message_id not found
405Method Not AllowedWrong HTTP method used for this endpoint
429Too Many RequestsRate limit exceeded (60 requests/minute per API key)
500Internal Server ErrorUnexpected server-side error
503Service UnavailableUpstream SMS carrier is temporarily unreachable — retry shortly

Testing Tools

Every example above is copy-pasteable. To confirm your setup end-to-end without any risk of a real charge, start with Test Connection (no key needed) then Get Balance (read-only) before attempting a real Send.

# 1. Confirm the API is reachable
curl https://sms.4knet.co.ke/api/v1/test

# 2. Confirm your key works and check your balance
curl -H "Authorization: Bearer YOUR_API_KEY" https://sms.4knet.co.ke/api/v1/balance

# 3. Send a real test message to your own number
curl -X POST https://sms.4knet.co.ke/api/v1/sms/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"recipient":"2547XXXXXXXX","message":"Test message"}'

Frequently Asked Questions

What phone number formats are accepted?

We accept 2547XXXXXXXX, 2541XXXXXXXX, or a local 07XXXXXXXX / 01XXXXXXXX number — the API normalizes it to the 254 format automatically before sending.

How are messages charged?

By units: up to 160 characters is 1 unit; longer messages are billed at roughly 153 characters per additional unit (standard concatenated-SMS segmenting). Each unit costs your account's per-unit rate, shown in the Get Balance response.

What sender ID will my messages use?

Your account's default sender ID (also returned as default_sender_id from Get Balance). It's managed from your Profile page.

Is there a rate limit?

Yes — 60 requests per minute per API key across all /api/v1/* endpoints. Exceeding it returns HTTP 429.

How can I test without spending balance?

Test Connection needs no API key and never touches your account. Get Balance and Check Status are read-only. Only Send SMS, Bulk SMS, and the Billing Gateway route ever dispatch a real message and deduct balance.

What's the difference between the main API and the Billing Gateway?

Same underlying send logic — the Billing Gateway just also accepts the alternate parameter names (number, secret, etc.) and pipe-delimited plain-text responses that older billing/hotspot systems expect, instead of requiring a JSON body and Bearer header.

Need Help?

If you run into trouble integrating:

  • Check the error message in the API response — they're written to be specific and actionable
  • Verify your API key is correct and hasn't been revoked on your Developer page
  • Confirm your account isn't suspended and has sufficient balance
  • Double-check the phone number format against the FAQ above
Open a Support Ticket